用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler
平时我们在用多线程开发的时候少不了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 }
如果大家对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 }
好了,当你看到这两张图,你应该明白带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 }
三:自定义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观察一下,确实是工作线程,而不是
线程池,没骗你~~~
好了,本篇就说到这里,希望对你有帮助。
相关文章
- .NET之Hangfire快速入门和使用
- .NET Core使用NPOI导出复杂Word详解
- .NET使用HttpClient以multipart/form-data形式post上传文件及其相关参数
- .NET项目中NLog的配置与使用
- ASP.NET MVC使用Layui选择多图片上传
- ASP.NET MVC中使用jQuery Ajax通过FormData对象异步提交图片文件到服务端保存并返回保存的图片路径
- .NET中将集合M内非空参数值的参数按照参数名ASCII码从小到大排序(字典序),并使用URL键值对的格式(即key1=value1&key2=value2…)拼接成字符串stringA
- SQL中关于Join、Inner Join、Left Join、Right Join、Full Join、On、 Where区别
- Web Deploy配置和使用Visual Studio进行Web项目发布部署
- .Net WebApi中使用Session使用
- .NET之NPOI Excel数据导出和批量导入功能
- MySQL 查询重复数据,删除重复数据保留id最小的一条作为唯一数据
- 常用正则表达式
- Packets larger than max_allowed_packet are not allowed(mysql数据查询提示:不允许超过允许的最大数据包)解决方案
- .NET轻松实现支付宝服务窗网页授权并获取用户相关信息
- C#在window服务配置Log4Net.dll
- 致于即将逝去的2108年,2019年您好
- 关于:未能加载文件或程序集“ICSharpCode.SharpZipLib”或它的某一个依赖项异常的解决方案
- Visual Studio编辑器还原项目NuGet提示某 NuGet 程序包还原失败:找不到“xxx”版本的程序包“xxx”
- 微信公众平台网页登录授权多次重定向跳转,导致code使用多次问题