使用await
时,默认情况下会捕获SynchronizationContext
(如果存在),并使用该上下文执行await
(继续块)之后的代码块(这导致线程上下文切换)。
public async Task DoSomethingAsync()
{
// We are on a thread that has a SynchronizationContext here.
await DoSomethingElseAsync();
// We are back on the same thread as before here
//(well sometimes, depending on how the captured SynchronizationContext is implemented)
}
虽然这个默认值可能在您希望在异步操作完成后返回UI线程的UI上下文中有意义,但它似乎没有意义作为大多数其他方案的默认值。对于内部库代码来说当然没有意义,因为
答案 0 :(得分:2)
首先,await Task.Yield().ConfigureAwait(false)
无法工作,因为Yield
没有返回Task
。还有其他方法可以跳到池线程,但也不推荐使用它们,请检查"Why was “SwitchTo” removed from Async CTP / Release?"
如果您仍然想要这样做,那么如果原始线程上存在同步上下文,那么利用 ConfigureAwait(false)
pushes the continuation to a pool thread的事实就是一个很好的技巧,即使有这里没有异步:
static Task SwitchAsync()
{
if (SynchronizationContext.Current == null)
return Task.FromResult(false); // optimize
var tcs = new TaskCompletionSource<bool>();
Func<Task> yield = async () =>
await tcs.Task.ConfigureAwait(false);
var task = yield();
tcs.SetResult(false);
return task;
}
// ...
public async Task DoSomethingAsync()
{
// We are on a thread that has a SynchronizationContext here.
await SwitchAsync().ConfigureAwait(false);
// We're on a thread pool thread without a SynchronizationContext
await DoSomethingElseAsync(); // no need for ConfigureAwait(false) here
// ...
}
同样,这是 not 我自己广泛使用的东西。关于使用ConfigureAwait(false)
我已经similar concerns了。一个结果是,虽然ConfigureAwait(false)
可能不是普遍完美的,但只要您不关心同步上下文就将其与await
一起使用是可行的方法。这是.NET源代码本身紧随其后的准则。
另一个结果是,如果您担心DoSomethingElseAsync
内可能未正确使用ConfigureAwait(false)
的第三方代码,请执行以下操作:
public async Task DoSomethingAsync()
{
// We are on a thread that has a SynchronizationContext here.
await Task.Run(() => DoSomethingElseAsync()).ConfigureAwait(false);
// We're on a thread pool thread without a SynchronizationContext
await DoYetSomethingElseAsync(); // no need for ConfigureAwait(false) here
// ...
}
这将使用接受Task.Run
lambda的Func<Task>
覆盖,在池线程上运行它并返回一个未包装的任务。您只能为await
内的第一个DoSomethingAsync
执行此操作。潜在成本与SwitchAsync
相同:一个额外的线程切换,但代码更易读,结构更好。这是我在工作中使用的方法。
答案 1 :(得分:1)
有没有更好的方法来解决这个问题,而不是通过使用.ConfigureAwait(false)来混淆我的代码中的所有await调用?这很容易忘记并且使代码的可读性降低。
不是真的。没有任何“开箱即用”开关可以打开以改变该行为。有ConfigureAwaiter Checker ReSharper扩展可以提供帮助。另一种选择是滚动你自己的自定义扩展方法或SynchronizationContext
包装器来切换上下文,或者甚至是自定义的awaiter。