【问题标题】:Understanding the behavior of TaskScheduler.Current了解 TaskScheduler.Current 的行为
【发布时间】:2014-05-29 02:13:34
【问题描述】:

这是一个简单的 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) 在等待之前,currentTs = System.Threading.Tasks.SynchronizationContextTaskScheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext } { where = 2) 在等待之后,currentTs = System.Threading.Tasks.ThreadPoolTask​​Scheduler, thread = 9, context = System.Windows.Forms.WindowsFormsSynchronizationContext }

问题:为什么TaskScheduler.Currentawait之后从SynchronizationContextTaskScheduler变成ThreadPoolTaskScheduler

这基本上表现了TaskCreationOptions.HideScheduler for await 延续的行为,在我看来,这是出乎意料和不可取的。

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

AspNetSynchronizationContext and await continuations in ASP.NET.

【问题讨论】:

    标签: c# .net task-parallel-library async-await


    【解决方案1】:

    如果没有实际执行的任务,则TaskScheduler.CurrentTaskScheduler.Default 相同。换句话说,ThreadPoolTaskScheduler 实际上既充当线程池任务调度器,也充当 意思是“没有当前任务调度器”的值。

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

    await 捕获其上下文时,它会捕获同步上下文(而不是任务调度程序),并使用该syncctx 来恢复。因此,方法延续被发布到该 syncctx,后者在 UI 线程上执行它。

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

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

    【讨论】:

    • 这就是我认为它的工作原理,但不想在没有更多信息的情况下发表评论。 +1。
    • 我明白了,显然这就是TaskAwaiter 的工作方式。 IMO,他们为“流动”TaskScheduler.Current 提出的设计相当令人困惑:通常这不是您在逻辑上期望的那样。
    • 同意; TaskScheduler.Current 在设计时考虑了动态并行性,因此子任务从其父任务继承调度程序。这种默认行为对于异步任务来说是令人困惑的,这就是为什么我坚持在每个 StartNewContinueWith 中明确指定调度程序。
    猜你喜欢
    • 2011-10-11
    • 1970-01-01
    • 2017-11-08
    • 1970-01-01
    • 1970-01-01
    • 2016-09-28
    • 2019-10-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多