我有一个带有异步方法的库,我读到对于库,建议使用 ConfigureAwait(false)
。
例如,如果我有类似的东西:
public async Task myMethod01()
{
await myMethod02();
}
private async Task myMethod02()
{
await myMethod03();
}
private async Task myMethod03()
{
await anotherMetodAsync().ConfigureAwait(false);
}
库用户只能使用method01
,其他2种方法是私有的,因为它们是主要方法method01()
的辅助方法。
,但我不知道是否仅在链条呼叫的第一个方法中使用ConfigureAwait
,或者我应该在所有链条中使用。
tl; dr
是,为了确保库中的所有异步连续性都在线程池线程上执行(取决于Synchronization -context/taskscheduler(。
您想了解更多吗?
task.configureawait(boolean(
-
true
试图元使用异步方法的其余部分 回到原始上下文 -
false
计划线程池线程上的异步方法的其余部分
考虑以下WPF示例:WPF使用dispatchersynchronization context在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
标志对呼叫者没有影响。但是,当然,调用方法在呼叫者上运行的线程上运行(或至少开始运行(。
但是,仅在等待不完整的任务时才会发生当前上下文的捕获(当前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(核心,控制台,...(。
有关更多详细信息,请查看异步编程中的最佳实践(I.A。 ConfigureAwait
(在Stephen Cleary的这篇MSDN杂志上的文章中。
您应该在所有异步调用上使用ConfigureAwait(false)
。如果不执行此操作,则第一个异步调用(没有ConfigureAwait(false)
(将采用同步context,当您同步在该调用上同步等待时,这可能在某些情况下(例如在ASP.NET上(造成僵局。
我的建议是阅读Stephen Cleary撰写的这篇文章。您感兴趣的部分是:
使用
ConfigureAwait(false)
避免死锁是危险的 实践。您将不得不使用ConfigureAwait(false)
在封闭代码调用的所有方法的及时关闭中, 包括所有第三方和第二方代码。使用ConfigureAwait(false)
避免僵局充其量只是一个黑客(。