了解TaskScheduler.Current的行为

nos*_*tio 12 .net c# 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()
        {
            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();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

调试输出(单击按钮时):

{ 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 }

问题:为什么要在这之后TaskScheduler.Current改变?SynchronizationContextTaskSchedulerThreadPoolTaskSchedulerawait

这基本上表现出行为TaskCreationOptions.HideScheduler的await延续,这是意想不到的不良,在我看来.

这个问题是由我的另一个问题引发的:

AspNetSynchronizationContext并等待ASP.NET中的延续.

Ste*_*ary 13

如果没有执行任何实际任务,则与之TaskScheduler.Current相同TaskScheduler.Default.换句话说,ThreadPoolTaskScheduler实际上既作为线程池任务调度程序又作为"无当前任务调度程序"的值.

async委托的第一部分是使用SynchronizationContextTaskScheduler,显式调度的,并在UI线程上运行,同时具有任务调度程序和同步上下文.任务调度程序将委托转发到同步上下文.

当await捕捉它的上下文,它抓住了同步上下文(而不是任务调度程序),并使用该syncctx恢复.因此,方法continuation被发布到syncctx,它在UI线程上执行它.

当延续在UI线程上运行时,它的行为与事件处理程序非常相似; 委托直接执行,不包含在任务中.如果你TaskScheduler.Current在开头检查button1_Click,你会发现它也是ThreadPoolTaskScheduler.

顺便说一句,我建议您将此行为(直接执行委托,不包含在任务中)视为实现细节.

  • 同意; `TaskScheduler.Current`在设计时考虑了动态并行性,因此子任务从父任务继承调度程序.这种默认行为让异步任务感到困惑,这就是我坚持在每个`StartNew`和`ContinueWith`中明确指定调度程序的原因. (4认同)