相信大家对 Task 已经非常熟悉了,在 Task 底层有一个发动机,决定了它是涡轮增压还是自然吸气,它就是 TaskScheduler 抽象类,在框架下这个发动机有两个默认实现子类:ThreadPoolTaskScheduler 和 SynchronizationContextTaskScheduler,具体应用场景以及如何自定义子类,这篇刚好和大家分享一下。

一: ThreadPoolTaskScheduler

ThreadPoolTaskScheduler 是 Task 的默认机制,而且从名字上也可以看到它是一种基于 ThreadPool 的机制,从侧面也说明 Task 是基于 ThreadPool 的封装,如果想具体查看代码逻辑,可以通过 ILSpy 反编译一下代码:

protected internal override void QueueTask(Task task)
        {
             if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
             {
                 new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork)
                 {
                     IsBackground = true
                 }.Start(task);
                 return;
             }
             bool forceGlobal = (task.Options & TaskCreationOptions.PreferFairness) > TaskCreationOptions.None;
             ThreadPool.UnsafeQueueCustomWorkItem(task, forceGlobal);
         }

从上面代码中可以看到如下逻辑,如果当前 Task 的 TaskCreationOptions 设置为 LongRunning 的话,这个task就会委托到 Thread 中去执行,这样的好处显而易见,如果长时间运行的 task 占用着 ThreadPool 的线程,这时候ThreadPool为了保证线程充足,会再次开辟一些 Thread,如果耗时任务此时释放了,会导致ThreadPool线程过多,上下文切换频繁,不信的话,还可以用 windbg 验证一下。。。

static void Main(string[] args)
         {
             var task = Task.Factory.StartNew(() =>
             {
                 Console.WriteLine("hello world!!!");
             }, TaskCreationOptions.LongRunning);

             Console.Read();
         }



如何 取消 PROCESSLIST sleep 是什么_多线程

为了好做对比,我再把 TaskCreationOptions 枚举去掉,用 !threads 在看看。

static void Main(string[] args)
         {
             var task = Task.Factory.StartNew(() =>
             {
                 Console.WriteLine("hello world!!!");
             });

             Console.Read();
         }



如何 取消 PROCESSLIST sleep 是什么_反编译_02

好了,看完这两张图,你应该明白当是 LongRunning 的话,thread 中是没有(ThreadPool Worker)标记的,表明当前是单独开辟的线程,下面这张图很明显带有这种标识,表示当前是委托在 ThreadPool 中执行的。

二:SynchronizationContextTaskScheduler

从名字中可以看到,这是一个同步上下文的 TaskScheduler,原理就是把繁重的耗时工作丢给 ThreadPool,然后将更新UI的操作丢给UI任务队列,由 UIThread 来执行,具体也可以在源码中窥探一二。

protected internal override void QueueTask(Task task)
    {
        this.m_synchronizationContext.Post(SynchronizationContextTaskScheduler.s_postCallback, task);
    }

继续追代码,可以看到 s_postCallback 里面会执行 Invoke 函数,如下代码:

public virtual void Post(SendOrPostCallback d, object state)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
        }

有了这个基础再来实现代码逻辑,注意下面这段代码是不阻塞 UIThread 的哦。

private void button1_Click(object sender, EventArgs e)
         {
             Task task = Task.Factory.StartNew(() =>
             {
                 //复杂操作,等待10s
                 Thread.Sleep(10000);

             }).ContinueWith((t) =>
             {
                 button1.Text = "hello world";
             }, TaskScheduler.FromCurrentSynchronizationContext());
         }



如何 取消 PROCESSLIST sleep 是什么_队列_03

三:自定义TaskScheduler

有些朋友可能要问,这些 Scheduler 我用起来不爽,我想自定义一下,这个可以吗?当然!!!实现一个抽象类 TaskScheduler 的子类,想怎么玩就怎么玩,比如说我想让所有的 Task 都走独立的 Thread,杜绝使用 TheadPool,这样可以吗?当然了,不信你看。

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            var task = Task.Factory.StartNew(() =>
            {
                Console.WriteLine("hello world!!!");
            }, new CancellationToken(), TaskCreationOptions.None, new PerThreadTaskScheduler());

            Console.Read();
        }
    }

    /// <summary>
    /// 每个Task一个Thread
    /// </summary>
    public class PerThreadTaskScheduler : TaskScheduler
    {
        protected override IEnumerable<Task> GetScheduledTasks()
        {
            return null;
        }

        protected override void QueueTask(Task task)
        {
            var thread = new Thread(() =>
            {
                TryExecuteTask(task);
            });

            thread.Start();
        }

        protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
        {
            throw new NotImplementedException();
        }
    }
}

看到没有,自定义Task就是这么简单,其实最重要的就是实现 QueueTask 方法,接下来我可以用windbg观察一下,确实是工作线程,而不是线程池,没骗你~~~



如何 取消 PROCESSLIST sleep 是什么_并发编程_04