我在单独的程序集中遇到了关于异步扩展方法的怪异行为。
我们有以下内容:
EventGridEvent
的发送。目标是.NET Standard 2.0。该程序集引用Microsoft.Azure.EventGrid
。由于某种原因,从汇编编号中创建同步方法。 2至组装号1导致奇怪的行为。考虑一下装配号中的两个功能。 1:
public async Task PublishAsync(...)
{
await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...);
}
public void Publish(...)
{
_eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...).Wait();
}
如果我们从程序集编号中调用第一个方法, 2 PublishAsync().Wait()
,它将永远不会返回。但是,Publish()
将。但是,如果Publish()
调用PublishAsync().Wait()
,该方法也会挂起。
值得一提的是EventGridClient
包含LongRunningOperationRetryTimeout
,默认设置为30,将其忽略。它永远不会返回。
任何人都不知道是什么原因导致了这种行为?一种解决方法是复制代码,但我们希望避免这种情况。
提前感谢。
您绝对不应通过在返回的Wait()
上调用.Result
或Task
来阻止异步代码。 @Stephen Cleary解释了为什么on his blog。
当调用_eventGridClient.PublishEventsAsync
时,将捕获SynchronizationContext
。任务完成后,它将等待上下文可用,但是永远不会,因为您正在通过调用.Wait()
来阻止它。这会导致死锁。
您可能会避免通过调用ConfigureAwait(false)
来捕获上下文而遇到麻烦:
public async Task PublishAsync(...)
{
await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...)
.ConfigureAwait(false);
}
但是最好的解决方案仍然是完全不阻塞。异步代码应该是“一路异步”,如链接的博客文章中所述。
问题是调用方法正在UI线程上运行。通过像这样包装调用即可解决:Task.Run(() => ...).Wait()