我有一个带有异步方法的库,并且已阅读该库,建议使用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
还是我应该在所有方法中都使用。
答案 0 :(得分:1)
您应该在所有异步调用上使用ConfigureAwait(false)
。如果不这样做,则第一个异步调用(不带ConfigureAwait(false)
)将使用SynchronizationContext,并且在某些情况下(如ASP.NET),当您同步等待该调用时,这可能导致死锁。
我的建议是阅读Stephen Cleary撰写的this article。您感兴趣的部分是:
使用
ConfigureAwait(false)
避免死锁是很危险的 实践。您每次必须使用ConfigureAwait(false)
在阻塞代码调用的所有方法的传递性关闭中, 包括所有第三方代码。使用ConfigureAwait(false)
避免死锁充其量只是破解。)
答案 1 :(得分:1)
是,这是为了确保您在库代码中的所有异步延续都在线程池线程上执行(取决于SynchronizationContext / {{3} })。
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
)。