了解ConfigureAwait

vic*_*ico 3 c# asynchronous

试图了解我什么时候应该使用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.
}
Run Code Online (Sandbox Code Playgroud)

什么是上下文以及如何查看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();
}
Run Code Online (Sandbox Code Playgroud)

我以为上下文是Thread,但事实并非如此。

Evk*_*Evk 5

这里的上下文是SynchronizationContext. Await 会将延续(await 之后的其余方法)发布到当前上下文 ( SynchronizationContext.Current),如果它存在并且您没有使用ConfigureAwaitfalse。如果延续没有发布到上下文 - 它将在线程池线程上执行。在控制台应用程序中,默认情况下没有同步上下文,因此在您的测试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);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在Main方法的开头:

SynchronizationContext.SetSynchronizationContext(new MySynchornizationContext());
Run Code Online (Sandbox Code Playgroud)

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

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

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