【问题标题】:Await async with event handler等待与事件处理程序的异步
【发布时间】:2013-11-28 04:48:00
【问题描述】:

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

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;

我的问题是我不明白如何做到这一点,并且仍然可以等待(即,我希望能够像现在一样等待,但通过委托处理它)。这有意义吗?

【问题讨论】:

    标签: c# asynchronous msmq


    【解决方案1】:

    通常,当您想将一些非基于任务的异步模型转换为基于任务的异步模型时,如果还没有 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 如果不了解您正在处理的特定课程,我真的不能说。理想情况下,您只有一个在异步事件完成时将被调用的方法。如果事件是共享的,那么您需要确定完成的事件是否对应于“this”请求。完成任务后,您还需要删除处理程序,是的。
    • 或者,另一种方法是将一个处理程序永久附加到接收队列,其工作是确定哪个任务负责并正确设置。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-12-22
    • 1970-01-01
    相关资源
    最近更新 更多