C# 了解TaskScheduler.Current的行为

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()

下面是一个简单的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();
        }
    }
}
调试输出(单击按钮时):

{其中=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
for
wait
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 }