我应该在所有方法中使用 configure await 还是只在第一种方法中使用?
Should I use configure await in all methods or only in the first method?
我有一个带有异步方法的库,我已经阅读了关于库的内容,建议使用 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()
的辅助方法method01()
。
但我不知道是否只需要在链调用的第一个方法中使用 ConfigureAwait
还是应该在所有方法中使用。
您应该在所有异步调用中使用 ConfigureAwait(false)
。如果未完成此操作,第一个异步调用(没有 ConfigureAwait(false)
)将采用 SynchronizationContext,这可能会在您同步等待该调用时在某些情况下(如 ASP.NET)导致死锁。
我的建议是阅读 Stephen Cleary 撰写的 this article。您感兴趣的部分是:
Using ConfigureAwait(false)
to avoid deadlocks is a dangerous
practice. You would have to use ConfigureAwait(false)
for every await
in the transitive closure of all methods called by the blocking code,
including all third- and second-party code. Using
ConfigureAwait(false)
to avoid deadlock is at best just a hack).
tl;博士
是,需要它来确保库代码中的所有异步延续都在线程池线程上执行(取决于 SynchronizationContext/TaskScheduler使用中)。
想了解更多吗?
true
尝试将 异步方法的其余部分 编组回捕获的原始上下文
false
在线程池线程 上安排 异步方法的剩余部分
考虑以下 WPF 示例:
WPF 使用 DispatcherSynchronizationContext 在 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
标志对调用者没有影响。然而,被调用的方法 运行s(或至少开始 运行)在调用者 运行 所在的线程上,当然。
但是,只有在等待未完成的任务时才会捕获当前上下文(或者当前 SynchronizationContext
;如果为 null,则当前 TaskScheduler
)。如果 Task 同步完成,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 核心、控制台……)。
有关详细信息,请查看此 MSDN Magazine Article by Stephen Cleary 中的 异步编程最佳实践 (i.a。ConfigureAwait
)。 =24=]
我有一个带有异步方法的库,我已经阅读了关于库的内容,建议使用 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()
的辅助方法method01()
。
但我不知道是否只需要在链调用的第一个方法中使用 ConfigureAwait
还是应该在所有方法中使用。
您应该在所有异步调用中使用 ConfigureAwait(false)
。如果未完成此操作,第一个异步调用(没有 ConfigureAwait(false)
)将采用 SynchronizationContext,这可能会在您同步等待该调用时在某些情况下(如 ASP.NET)导致死锁。
我的建议是阅读 Stephen Cleary 撰写的 this article。您感兴趣的部分是:
Using
ConfigureAwait(false)
to avoid deadlocks is a dangerous practice. You would have to useConfigureAwait(false)
for every await in the transitive closure of all methods called by the blocking code, including all third- and second-party code. UsingConfigureAwait(false)
to avoid deadlock is at best just a hack).
tl;博士
是,需要它来确保库代码中的所有异步延续都在线程池线程上执行(取决于 SynchronizationContext/TaskScheduler使用中)。
想了解更多吗?
true
尝试将 异步方法的其余部分 编组回捕获的原始上下文false
在线程池线程 上安排 异步方法的剩余部分
考虑以下 WPF 示例: WPF 使用 DispatcherSynchronizationContext 在 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
标志对调用者没有影响。然而,被调用的方法 运行s(或至少开始 运行)在调用者 运行 所在的线程上,当然。
但是,只有在等待未完成的任务时才会捕获当前上下文(或者当前 SynchronizationContext
;如果为 null,则当前 TaskScheduler
)。如果 Task 同步完成,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 核心、控制台……)。
有关详细信息,请查看此 MSDN Magazine Article by Stephen Cleary 中的 异步编程最佳实践 (i.a。ConfigureAwait
)。 =24=]