【问题标题】:Understanding ConfigureAwait了解 ConfigureAwait
【发布时间】:2021-03-28 10:53:12
【问题描述】:

试图了解何时应该使用ConfigureAwait()
根据书:

async 方法在等待后恢复时,默认情况下它将在同一上下文中恢复执行。如果该上下文是 UI 上下文并且大量异步方法正在 UI 上下文上恢复,这可能会导致性能问题。

解决方案

为避免在上下文中恢复,等待ConfigureAwait() 的结果并为其continueOnCapturedContext 参数传递false:

async Task ResumeWithoutContextAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(false);

    //This method discards its context when it resumes.
}

什么是上下文以及如何查看ConfigureAwait() 更改示例应用程序中的内容:

static async Task ResumeWithoutContextAsync()
{
    await Task.Delay(TimeSpan.FromSeconds(1)).ConfigureAwait(true);
    Console.WriteLine("ManagedThreadId {0}", Thread.CurrentThread.ManagedThreadId);

    //This method discards its context when it resumes.
}

static void Main(string[] args)
{
    ResumeWithoutContextAsync();

    Console.ReadLine();
}

我以为上下文是Thread,但不是。

【问题讨论】:

  • 有一个术语叫“阻塞”。一些方法在工作完成时同步(阻塞)返回,而另一些方法是异步的(非阻塞)并在所有工作完成之前返回。当方法是非阻塞的并且你需要等到所有工作完成时才需要等待。

标签: c# asynchronous


【解决方案1】:

这里的上下文是SynchronizationContext。如果存在并且您没有使用 ConfigureAwait false,则 Await 会将继续(等待之后的方法的其余部分)发布到当前上下文 (SynchronizationContext.Current)。如果延续未发布到上下文 - 它将在线程池线程上执行。在控制台应用程序中,默认情况下没有同步上下文,因此在您的测试中 ConfigureAwait 无效。您可以创建虚拟上下文来查看效果:

class MySynchornizationContext : SynchronizationContext {
    public override void Post(SendOrPostCallback d, object state) {
        Console.WriteLine("posted");
        base.Post(d, state);
    }

    public override void Send(SendOrPostCallback d, object state) {
        Console.WriteLine("sent");
        base.Send(d, state);
    }
}

然后在Main方法的开头:

SynchronizationContext.SetSynchronizationContext(new MySynchornizationContext());

使用ConfigureAwait(true)(或根本不使用)- 您将看到延续已发布到上下文(控制台中的“已发布”行)。使用ConfigureAwait(false) - 你会发现它不是。

当然,真正的同步上下文比那更复杂。例如,UI 上下文(如在 winforms 或 WPF 中)将“排队”延续并在一个(UI)线程上执行它们。如您的问题引用中所述,由于各种原因(并且可能导致死锁),这可能存在问题,因此当您编写通用库时 - 使用 ConfigureAwait(false 来避免这种行为是有益的。

SynchronizationContext 当然不需要将所有回调发布到单个线程,它可以对它们做任何事情。例如 ASP.NET MVC 上下文(至少是旧版本)会将回调发布到请求线程,并且请求可以有很多请求线程。

【讨论】:

    【解决方案2】:

    今天,我们主要在通过 REST 调用访问的快速服务中编写 JSON 服务。本机 GUI 应用程序和 IIS 应用程序服务器需要异步任务在原始线程上下文中恢复执行。不幸的是,这种对上世纪开发的偏见迫使我们在服务代码中的几乎每个调用中都使用“ConfigureAwait(false)”。

    最后,这是一个非常令人困惑和误导的名称。它读作“做或不配置等待”,但实际上它是“线程亲缘关系真或假”。没有其他选项,因此它不是一般的配置方法。

    【讨论】:

    • 不一定表示“线程亲和性真假”。可以在不同的线程上输入相同的同步上下文,AspNetSynchronizationContext 就是这种情况。
    猜你喜欢
    • 2018-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    • 2020-10-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多