这是一些WinForms代码:
async void Form1_Load(object sender, EventArgs e)
{
// on the UI thread
Debug.WriteLine(new { where = "before",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
var tcs = new TaskCompletionSource<bool>();
this.BeginInvoke(new MethodInvoker(() => tcs.SetResult(true)));
await tcs.Task.ContinueWith(t => {
// still on the UI thread
Debug.WriteLine(new { where = "ContinueWith",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false);
// on a pool thread
Debug.WriteLine(new { where = "after",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}
输出结果:
{ where = before, ManagedThreadId = 10, IsThreadPoolThread = False } { where = ContinueWith, ManagedThreadId = 10, IsThreadPoolThread = False } { where = after, ManagedThreadId = 11, IsThreadPoolThread = True }
为什么 ConfigureAwait 在这里主动地将 await
继续执行推到池线程中呢?
这里使用"推送到池线程"来描述以下情况:当主继续回调(即传递给 TaskAwaiter.UnsafeOnCompleted
的action
参数)在一个线程上被调用时,而次要回调(传递给 ConfiguredTaskAwaiter.UnsafeOnCompleted
的回调函数)则排队到池线程。
文档中说:
continueOnCapturedContext...true表示尝试将继续返回到捕获的原始上下文;否则,为false。
我知道当前线程上安装了WinFormsSynchronizationContext
。但是,这里没有尝试进行任何控制转移,执行点已经在那里。
因此,更像是 "从未在捕获的原始上下文上继续运行"...
如果执行点已经位于没有同步上下文的池线程上,则不会发生线程切换,正如预期的那样:
await Task.Delay(100).ContinueWith(t =>
{
// on a pool thread
Debug.WriteLine(new { where = "ContinueWith",
Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread });
}, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false);
{ where = before, ManagedThreadId = 10, IsThreadPoolThread = False } { where = ContinueWith, ManagedThreadId = 6, IsThreadPoolThread = True } { where = after, ManagedThreadId = 6, IsThreadPoolThread = True }
更新:再进行一次测试以查看是否任何同步上下文都不足以实现继续操作(而不是原始的同步上下文)。事实证明确实如此:
class DumbSyncContext: SynchronizationContext
{
}
// ...
Debug.WriteLine(new { where = "before",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread });
var tcs = new TaskCompletionSource<bool>();
var thread = new Thread(() =>
{
Debug.WriteLine(new { where = "new Thread",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread});
SynchronizationContext.SetSynchronizationContext(new DumbSyncContext());
tcs.SetResult(true);
Thread.Sleep(1000);
});
thread.Start();
await tcs.Task.ContinueWith(t => {
Debug.WriteLine(new { where = "ContinueWith",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread});
}, TaskContinuationOptions.ExecuteSynchronously).ConfigureAwait(false);
Debug.WriteLine(new { where = "after",
Thread.CurrentThread.ManagedThreadId,
Thread.CurrentThread.IsThreadPoolThread });
{ where = before, ManagedThreadId = 9, IsThreadPoolThread = False } { where = new Thread, ManagedThreadId = 10, IsThreadPoolThread = False } { where = ContinueWith, ManagedThreadId = 10, IsThreadPoolThread = False } { where = after, ManagedThreadId = 6, IsThreadPoolThread = True }
Task.ConfigureAwait(continueOnCapturedContext: false)
。 - noseratio - open to work