我应该在所有方法中还是仅在第一种方法中使用configure await?

时间:2019-05-25 10:55:12

标签: c# async-await configureawait

我有一个带有异步方法的库,并且已阅读该库,建议使用ConfigureAwait(false)

例如,如果我有类似的东西:

public async Task myMethod01()
{
    await myMethod02();
}

private async Task myMethod02()
{
    await myMethod03();
}

private async Task myMethod03()
{
    await anotherMetodAsync().ConfigureAwait(false);
}

库用户只能使用method01,其他两种方法是私有方法,因为它们是主要方法method01()的辅助方法。

但是我不知道是否仅在链调用的第一种方法中需要使用ConfigureAwait还是我应该在所有方法中都使用。

2 个答案:

答案 0 :(得分:1)

您应该在所有异步调用上使用ConfigureAwait(false)。如果不这样做,则第一个异步调用(不带ConfigureAwait(false))将使用SynchronizationContext,并且在某些情况下(如ASP.NET),当您同步等待该调用时,这可能导致死锁。

我的建议是阅读Stephen Cleary撰写的this article。您感兴趣的部分是:

  

使用ConfigureAwait(false)避免死锁是很危险的   实践。您每次必须使用ConfigureAwait(false)   在阻塞代码调用的所有方法的传递性关闭中,   包括所有第三方代码。使用   ConfigureAwait(false)避免死锁充其量只是破解。)

答案 1 :(得分:1)

tl; dr

,这是为了确保您在库代码中的所有异步延续都在线程池线程上执行(取决于SynchronizationContext / {{3} })。

您想了解更多吗?

TaskScheduler

  • true尝试将异步方法的其余部分编组回捕获的原始上下文
  • false在线程池线程上调度异步方法的其余部分

请考虑以下WPF示例: WPF使用Task.ConfigureAwait(Boolean)在UI上下文上恢复异步继续,因为后台线程无法更新控件的内容。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteAsynchronously();

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteAsynchronously().ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}

private async Task CompleteAsynchronously()
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //true
}

在这里,您看到被调用方法的continueOnCapturedContext标志对调用者没有影响。但是,当然,被调用方法在调用者正在其上运行的线程上运行(或至少开始运行)。

但是,仅当等待不完整的Task时才发生当前上下文(当前SynchronizationContext;如果为null则当前TaskScheduler)的捕获。如果任务同步完成,则continueOnCapturedContext无效,该方法的其余部分继续在当前线程上同步运行。

private async void Button_Click(object sender, RoutedEventArgs e)
{
    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context

    await CompleteSynchronously().ConfigureAwait(false);

    logger.LogInformation(Thread.CurrentThread.IsThreadPoolThread); //false -> GUI context
}

private async Task CompleteSynchronously()
{
    await Task.Delay(0);
}

因此,在您的库代码中(假设您永远不需要上下文),您应该始终使用ConfigureAwait(false)以确保没有上下文被捕获以进行异步延续,而不考虑框架调用了程序集(例如WPF) ,ASP.NET Core,控制台等)。

有关更多详细信息,请参阅DispatcherSynchronizationContext撰写的MSDN Magazine Article中的异步编程最佳实践(即ConfigureAwait)。