等待异步事件处理程序的 await async with

3

我对如何最好处理这种情况感到困惑。我想等待异步调用的响应。具体而言,我有:

public async Task<IApiData> FetchQueuedApiAsync(TimeSpan receiveTimeout)
{
    var message = await Task.Factory.FromAsync<Message>(
        ReadQueue.BeginReceive(receiveTimeout), ReadQueue.EndReceive);
    return message.Body as IApiData;
 }

这个代码可以正常运行。但是,如果超时了,就不能调用ReadQueue.EndReceive,否则会抛出异常。显然,FromAsync不知道这一点,因此盲目调用它——引发异常。不调用EndReceive也是MSMQ的要求,超出了我的控制范围。

当消息准备好或超时发生时,队列会暴露一个事件。通常在这里检查消息是否存在,然后根据文档调用EndReceive。标准的监听器....

ReadQueue.ReceiveCompleted += ReadQueue_ReceiveCompleted;

我的问题是我不知道如何既能做到这一点,又能保持此为可等待状态(即我想能够像现在一样等待,但通过委托处理它)。这有意义吗?

1个回答

9

通常当你想将一些非任务异步模型转换为任务异步模型时,如果没有一个现成的Task方法来帮助你进行转换,你需要手动使用TaskCompletionSource来处理每个情况:

public Task<IApiData> FetchQueuedApiAsync(TimeSpan receiveTimeout)
{
    var tcs = new TaskCompletionSource<IApiData>();

    ReadQueue.BeginReceive(receiveTimeout);
    ReadQueue.ReceiveCompleted += (sender, args) =>
    {
        if (timedOut)
            tcs.TrySetCanceled();
        else if (threwException)
            tcs.TrySetException(exception);
        else
            tcs.TrySetResult(ReadQueue.EndReceive() as IApiData);
    };
    return tcs.Task;
}

根据您没有展示的类型具体情况,您需要进行相应的调整,但这是一般的想法。


我认为这看起来像我想要的。我不确定这里的事件监听器是如何工作的。每次调用函数时,它会向ReceiveCompleted添加一个新处理程序吗?考虑到这被频繁调用,我需要在某个地方进行清理吗?ReadQueue也在几个并发线程之间共享。如果多个线程都在侦听ReceiveCompleted,它们是否都会收到事件的触发? - AndySavage
@AndySavage 如果不了解你具体处理的类,我无法做出详细说明。理想情况下,只需一个方法即可在异步事件完成时调用。如果该事件是共享的,那么需要确定完成事件是否对应于“此”请求。当任务完成后,您还需要删除处理程序。 - Servy
或者,另一种方法是永久性地将一个处理程序附加到接收队列上,其工作是找出哪个任务负责并正确设置它。 - AnotherParker

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接