C# 了解TaskScheduler.Current的行为
下面是一个简单的WinForms应用程序:C# 了解TaskScheduler.Current的行为,c#,.net,task-parallel-library,async-await,C#,.net,Task Parallel Library,Async Await,下面是一个简单的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()
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();
}
}
}
调试输出(单击按钮时):
{其中=1)在等待之前,currentTs=System.Threading.Tasks.SynchronizationContextTaskScheduler,thread=9,context=System.Windows.Forms.WindowsFormsSynchronizationContext}
{其中=2)在等待之后,currentTs=System.Threading.Tasks.ThreadPoolTaskScheduler,thread=9,context=System.Windows.Forms.WindowsFormsSynchronizationContext}
问题:为什么TaskScheduler.Current
在等待后从SynchronizationContextTaskScheduler
更改为ThreadPoolTaskScheduler
这本质上展示了TaskCreationOptions.HideScheduler
forwait
continuation的行为,在我看来,这是意外和不受欢迎的
这个问题是由我的另一个问题引发的:
。换句话说,ThreadPoolTaskScheduler
实际上同时充当线程池任务调度器和表示“无当前任务调度器”的值
async
委托的第一部分使用SynchronizationContextTaskScheduler
进行显式调度,并在具有任务调度器和同步上下文的UI线程上运行。任务计划程序将委托转发到同步上下文
当wait
捕获其上下文时,它将捕获同步上下文(而不是任务调度程序),并使用该syncctx来恢复。因此,方法延续被发布到syncctx,后者在UI线程上执行它
当延续在UI线程上运行时,它的行为非常类似于事件处理程序;委托直接执行,而不是包装在任务中。如果在按钮1的开头选中TaskScheduler.Current
,您会发现它也是ThreadPoolTaskScheduler
顺便说一句,我建议您将此行为(直接执行委托,而不是包装在任务中)视为一个实现细节。我认为它是这样工作的,但不想在没有更多信息的情况下进行评论+1.我明白了,显然这就是TaskAwaiter
的工作原理。依我看,他们为“流动的”TaskScheduler设计的。当前的
相当混乱:通常它不是你逻辑上期望的那样。同意TaskScheduler.Current
设计时考虑了动态并行性,因此子任务从父任务继承调度程序。对于异步任务来说,这种默认行为令人困惑,这就是为什么我坚持在每个StartNew
和ContinueWith
中显式指定调度程序的原因。
{ where = 1) before await, currentTs = System.Threading.Tasks.SynchronizationContextTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext }
{ where = 2) after await, currentTs = System.Threading.Tasks.ThreadPoolTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext }