了解TaskScheduler.Current的行为
这是一个简单的WinForms应用程序:
using System; using System.Diagnostics; using System.Threading; using System.Threading.Tasks; using System.Windows.Forms; namespace WindowsFormsApplication { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private async void button1_Click(object sender, EventArgs e) { var ts = TaskScheduler.FromCurrentSynchronizationContext(); await Task.Factory.StartNew(async () => { Debug.WriteLine(new { where = "1) before await", currentTs = TaskScheduler.Current, thread = Thread.CurrentThread.ManagedThreadId, context = SynchronizationContext.Current }); await Task.Yield(); // or await Task.Delay(1) Debug.WriteLine(new { where = "2) after await", currentTs = TaskScheduler.Current, thread = Thread.CurrentThread.ManagedThreadId, context = SynchronizationContext.Current }); }, CancellationToken.None, TaskCreationOptions.None, scheduler: ts).Unwrap(); } } }
调试输出(单击按钮时):
{where = 1}在await之前,currentTs = System.Threading.Tasks.SynchronizationContextTaskScheduler,thread = 9,context = System.Windows.Forms.WindowsFormsSynchronizationContext} {where = 2}等待之后,currentTs = System.Threading.Tasks.ThreadPoolTaskScheduler,thread = 9,context = System.Windows.Forms.WindowsFormsSynchronizationContext}
问题:为什么在await
这里之后TaskScheduler.Current
从SynchronizationContextTaskScheduler
更改为ThreadPoolTaskScheduler
?
这基本上展示了用于await
延续的行为TaskCreationOptions.HideScheduler
,在我看来这是意想不到的和不可取的。
这个问题是由我的另一个问题引发的:
AspNetSynchronizationContext并等待ASP.NET中的延续 。
如果没有执行任何实际任务 ,则TaskScheduler.Current
与TaskScheduler.Default
相同。 换句话说, ThreadPoolTaskScheduler
实际上既作为线程池任务调度程序又作为“无当前任务调度程序”的值。
async
委托的第一部分使用SynchronizationContextTaskScheduler
显式调度,并在UI线程上运行,同时具有任务调度程序和同步上下文。 任务调度程序将委托转发到同步上下文。
当await
捕获其上下文时,它捕获同步上下文(而不是任务调度程序),并使用该syncctx恢复。 因此,方法continuation被发布到syncctx,它在UI线程上执行它。
当延续在UI线程上运行时,它的行为与事件处理程序非常相似; 委托是直接执行的,不包含在任务中。 如果你在button1_Click
的开头检查TaskScheduler.Current
,你会发现它也是ThreadPoolTaskScheduler
。
顺便说一句,我建议您将此行为(直接执行委托,不包含在任务中)视为实现细节。