从单独的程序集调用异步扩展方法



我遇到了关于单独程序集中的异步扩展方法的奇怪行为。

我们有以下几点:

  1. 一个处理EventGridEvent发送的组件。目标是 .NET Standard 2.0。此程序集引用Microsoft.Azure.EventGrid
  2. 一个使用程序集编号 1 的组件。目标是 .NET Framework 4.7。

出于某种原因,创建从程序集 2 到程序集 1 的同步方法会导致奇怪的行为。考虑我们在汇编 1 中的两个函数:

public async Task PublishAsync(...)
{
await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...);
}
public void Publish(...) 
{
_eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...).Wait();
}

如果我们用PublishAsync().Wait()调用汇编 2 中的第一个方法,它将永远不会返回。 然而,Publish()会的。但是,如果Publish()调用PublishAsync().Wait(),该方法也将挂起。

值得一提的是,EventGridClient包含默认值设置为 30 的LongRunningOperationRetryTimeout,该被忽略。它永远不会回来。

有人知道是什么原因导致这种行为吗?解决方法是复制代码,但我们希望避免这种情况。

提前谢谢。

切勿通过在返回的Task上调用Wait().Result来阻止异步代码。 @Stephen Cleary在他的博客上解释了原因。

调用_eventGridClient.PublishEventsAsync时,将捕获SynchronizationContext。当任务完成时,它会等待上下文变得可用,但它永远不会,因为您通过调用.Wait()来阻止它。这会导致僵局。

通过调用ConfigureAwait(false)来避免捕获上下文,您可能会遇到麻烦:

public async Task PublishAsync(...)
{
await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...)
.ConfigureAwait(false);
}

但最好的解决方案仍然是根本不阻止。异步代码应该是"一路异步",如链接的博客文章中所述。

问题是调用方法在 UI 线程上运行。通过像这样包装调用来解决:Task.Run(() => ...).Wait()

最新更新