2013-11-27 162 views
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; 

我的问题是,我不知道如何做到这一点,还是有这个作为awaitable(即我希望能够像等待我现在有,但通过处理它代表)。那有意义吗?

回答

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; 
} 

你需要根据你并没有表现出类型的细节来调整这一点,但这是一般的想法。

+0

我认为这看起来像我想要的。我不确定事件侦听器如何在这里工作。每次调用函数时,是否在ReceiveCompleted中添加了新的处理函数?鉴于这被称为很多,我是否需要在某个地方清理它? ReadQueue也在几个并发线程之间共享。如果多个线程正在侦听ReceiveCompleted,他们是否都会向他们提供事件? – AndySavage

+0

@AndySavage如果不知道更多关于你正在处理的具体课程,我不能说真的。理想情况下,只有一个方法会在异步事件完成时调用。如果事件是共享的,那么您需要确定已完成的事件是否与“此”请求相对应。当你完成任务时,你也想删除处理程序,是的。 – Servy

+0

或者,另一种方法是将一个处理程序永久附加到接收队列,该接收队列的任务是确定哪个任务是负责任的,并将其正确设置。 – AnotherParker