用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler

用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler平时我们在用多线程开发的时候少不了Task,确实task给我们带来了巨大的编程效率,在Task底层有一个TaskScheduler,它决定了task该如何被调度,而在.netframework中有

大家好,又见面了,我是你们的朋友全栈君。

  平时我们在用多线程开发的时候少不了Task,确实task给我们带来了巨大的编程效率,在Task底层有一个TaskScheduler,它决定了task该如何被调度,而

在.net framework中有两种系统定义Scheduler,第一个是Task默认的ThreadPoolTaskScheduler,还是一种就是SynchronizationContextTaskScheduler,

以及这两种类型之外的如何自定义,这篇刚好和大家分享一下。

 

一: ThreadPoolTaskScheduler

      这种scheduler机制是task的默认机制,而且从名字上也可以看到它是一种委托到ThreadPool的机制,刚好也从侧面说明task是基于ThreadPool基础上的

封装,如果想具体查看代码逻辑,你可以通过ILSpy反编译一下代码看看:

 1   protected internal override void QueueTask(Task task)
 2          {
 3              if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
 4              {
 5                  new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork)
 6                  {
 7                      IsBackground = true
 8                  }.Start(task);
 9                  return;
10              }
11              bool forceGlobal = (task.Options & TaskCreationOptions.PreferFairness) > TaskCreationOptions.None;
12              ThreadPool.UnsafeQueueCustomWorkItem(task, forceGlobal);
13          }

 

       从上面的代码中可以看到如下逻辑,如果当前Task上的TaskCreationOptions设置为LongRunning的话,这个task就会委托到Thread中去执行,这样的

好处显而易见,如果长时间运行的task占用着ThreadPool的线程,这时候ThreadPool为了保证线程充足,会再次开辟一些Thread,如果耗时任务此时释放了,

会导致ThreadPool线程过多,上下文切换频繁,所以这种情况下让Task在Thread中执行还是非常不错的选择,当然如果你不指定这个LongRunning的话,那就

是在ThreadPool上执行,不信的话,还可以用windbg去验证一下。。。

1     static void Main(string[] args)
2          {
3              var task = Task.Factory.StartNew(() =>
4              {
5                  Console.WriteLine("hello world!!!");
6              }, TaskCreationOptions.LongRunning);
7  
8              Console.Read();
9          }

<span role="heading" aria-level="2">用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler

 

如果大家对windbg不熟悉的话,也没关系,先暂且不讨论,我们只要把TaskCreationOptions枚举去掉,然后用这种形式的!threads给大家展示下不同

应该就非常明朗了。

1        static void Main(string[] args)
2          {
3              var task = Task.Factory.StartNew(() =>
4              {
5                  Console.WriteLine("hello world!!!");
6              });
7  
8              Console.Read();
9          }

 

<span role="heading" aria-level="2">用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler

 

好了,当你看到这两张图,你应该明白带LongRunning的话,thread中没有带(ThreadPool Worker)标记,也就表明当前是单独开辟的线程,而下面

这张图很明显带有这种标识,表示当前是委托在ThreadPool中执行的。

 

二:SynchronizationContextTaskScheduler

     从这个名字中就可以看到,这是一个同步上下文的taskscheduler,原理就是把繁重的耗时工作丢给ThreadPool,然后将更新UI的操作丢给 UI线程的

队列中,由UIThread来执行,具体的也可以在这种scheduler中窥得一二。

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

     然后可以从s_postCallback上看到里面有一个Invoke函数,如下图:

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

      有了这个基础我们再来看一下代码怎么写,可以看到,下面这段代码是不阻塞UIThread的,完美~~~

 1          private void button1_Click(object sender, EventArgs e)
 2          {
 3              Task task = Task.Factory.StartNew(() =>
 4              {
 5                  //复杂操作,等待10s
 6                  Thread.Sleep(10000);
 7  
 8              }).ContinueWith((t) =>
 9              {
10                  button1.Text = "hello world";
11              }, TaskScheduler.FromCurrentSynchronizationContext());
12          }

 

<span role="heading" aria-level="2">用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler

 

三:自定义TaskScheduler 

  我们知道在现有的.net framework中只有这么两种TaskScheduler,有些同学可能想问,这些Scheduler我用起来不爽,我想自定义一下,这个可

以吗?当然!!!如果你想自定义,只要自定义一个类实现一下TaskScheduler就可以了,然后你可以将ThreadPoolTaskScheduler简化一下,即我要

求所有的Task都需要走Thread,杜绝使用TheadPool,这样可以吗,当然了,不信你看。

 1 namespace ConsoleApplication1
 2 {
 3     class Program
 4     {
 5         static void Main(string[] args)
 6         {
 7             var task = Task.Factory.StartNew(() =>
 8             {
 9                 Console.WriteLine("hello world!!!");
10             }, new CancellationToken(), TaskCreationOptions.None, new PerThreadTaskScheduler());
11 
12             Console.Read();
13         }
14     }
15 
16     /// <summary>
17     /// 每个Task一个Thread
18     /// </summary>
19     public class PerThreadTaskScheduler : TaskScheduler
20     {
21         protected override IEnumerable<Task> GetScheduledTasks()
22         {
23             return null;
24         }
25 
26         protected override void QueueTask(Task task)
27         {
28             var thread = new Thread(() =>
29             {
30                 TryExecuteTask(task);
31             });
32 
33             thread.Start();
34         }
35 
36         protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
37         {
38             throw new NotImplementedException();
39         }
40     }
41 }

 

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

线程池,没骗你~~~

<span role="heading" aria-level="2">用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler

 

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

 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/155806.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • yourphp目录结构

    yourphp目录结构

    2021年10月9日
  • latex 希腊字母输入「建议收藏」

    latex 希腊字母输入「建议收藏」数学公式离不开希腊字母,以下列出了LaTex中产生希腊字母的控制命令:  \alpha产生字符α;\beta产生字符β;\gamma产生字符γ;\delta产生字符δ;\epsilon产生字符ε;\zeta产生字符ζ;\eta产生字符η;\theta产生字符9;\iota产生字符ιt;\kappa产生字符κ;\1ambda产生字符λ;\mu产生字符μ;\xi产生字符ξ:\nu产生字符ν

    2022年10月13日
  • linux jdk安装步骤(下载安装jdk)

    1.linux虚拟机需要先连接互联网2.命令行下载jdk安装包3.jdk安装包解压安装及环境配置4.直接在线安装jdk并配置运行环境。1.查看yum库中都有哪些jdk版本(暂时只发现了openjdk)[root@localhost~]#yumsearchjava|grepjdk2.选择版本,进行安装//选择1.7版本进行安装[root@localhost~]#yuminstalljava-1.7.0-openjdk[root@localhost~]#yumins

    2022年4月11日
  • scrapy爬虫完整的代码实例[通俗易懂]

    scrapy爬虫完整的代码实例[通俗易懂]新建工程scrapystartprojecttutorial进入tutorial目录,在spider下面新建quotes_spider.pyimportscrapyfrom..itemsimportQuotesItem#coding:utf-8classQuotesSpider(scrapy.Spider):name=”quot…

    2022年6月26日
  • 数据库置疑问题解决

    数据库置疑问题解决资料一1、停止数据库server,将数据库MDF文件和LDF文件复制备份一份2、启动数据库server,删除置疑的数据库3、仅用备份的数据库MDF文件附加数据库,sp_attach_db或者sp_at

    2022年7月2日
  • Visio2007产品密钥

    Visio2007产品密钥注意:以下是一个产品密钥,全部粘贴,不是6选一W2JJW-4KYDP-2YMKW-FX36H-QYVD8QGRC3-KQF42-GXR8T-MJQJ7-76938H22HP-9PGYQ-FKHQ9-KYH6K-JPJ7WVMRGQ-G3YMP-RWYH2-4TQ97-CT2HDV9MTG-3GX8P-D3Y4R-68BQ8-4Q8VDW2JJW-4KYDP-2YMKW-FX36H-QYVD8…

    2022年6月24日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号