我遇到了关于单独程序集中的异步扩展方法的奇怪行为。
我们有以下几点:
- 一个处理
EventGridEvent
发送的组件。目标是 .NET Standard 2.0。此程序集引用Microsoft.Azure.EventGrid
。 - 一个使用程序集编号 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()