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

Calling async extension methods from separate assembly

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

我们有以下内容:

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

出于某种原因,从程序集 no. 制作同步方法。 2 至装配编号1 导致奇怪的行为。考虑我们在第 1 号装配体中的两个功能。 1:

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

    public void Publish(...) 
    {
        _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...).Wait();
    }

如果我们从第一个程序集调用第一个方法。 2 与 PublishAsync().Wait(),它永远不会 return。但是,Publish() 会。但是,如果 Publish() 调用 PublishAsync().Wait(),该方法也会挂起。

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

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

提前致谢。

您永远不应该通过在返回的 Task 上调用 Wait().Result 来阻塞异步代码。 @Stephen Cleary 解释了为什么 on his blog.

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

您可以通过调用 ConfigureAwait(false):

避免捕获上下文来摆脱麻烦
public async Task PublishAsync(...)
{
    await _eventGridClient.PublishEventsAsync(_eventGridTopicHostName, ...)
        .ConfigureAwait(false);
}

但最好的解决办法还是完全不阻塞。如链接博客 post.

中所述,异步代码应为 "async all the way"

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