8288分类目录 8288分类目录 8288分类目录
  当前位置:海洋目录网 » 站长资讯 » 教育考试 » 文章详细 订阅RssFeed

浅议 Task 底层的调度机制 TaskScheduler

来源:本站原创 浏览:92次 时间:2022-12-26

相信大家对 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();
         }

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


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

             Console.Read();
         }

好了,看完这两张图,你应该明白当是 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());
         }

三:自定义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观察一下,确实是工作线程,而不是线程池,没骗你~~~

好了,本篇就说到这里,希望对你有帮助。


  推荐站点

  • At-lib分类目录At-lib分类目录

    At-lib网站分类目录汇集全国所有高质量网站,是中国权威的中文网站分类目录,给站长提供免费网址目录提交收录和推荐最新最全的优秀网站大全是名站导航之家

    www.at-lib.cn
  • 中国链接目录中国链接目录

    中国链接目录简称链接目录,是收录优秀网站和淘宝网店的网站分类目录,为您提供优质的网址导航服务,也是网店进行收录推广,站长免费推广网站、加快百度收录、增加友情链接和网站外链的平台。

    www.cnlink.org
  • 35目录网35目录网

    35目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向35目录推荐、提交优秀网站。

    www.35mulu.com
  • 就要爱网站目录就要爱网站目录

    就要爱网站目录,按主题和类别列出网站。所有提交的网站都经过人工审查,确保质量和无垃圾邮件的结果。

    www.912219.com
  • 伍佰目录伍佰目录

    伍佰网站目录免费收录各类优秀网站,全力打造互动式网站目录,提供网站分类目录检索,关键字搜索功能。欢迎您向伍佰目录推荐、提交优秀网站。

    www.wbwb.net