平时我们在用多线程开发的时候少不了 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 voidMain(string[] args)
- 2 {
- 3 vartask = Task.Factory.StartNew(() =>4 {
- 5Console.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 voidPost(SendOrPostCallback d,object state)
- 2 {
- 3ThreadPool.QueueUserWorkItem(new WaitCallback(d.Invoke), state);
- 4}
有了这个基础我们再来看一下代码怎么写,可以看到,下面这段代码是不阻塞 UIThread 的,完美~~~
- 1 private voidbutton1_Click(object sender, EventArgs e)
- 2 {
- 3Task task = Task.Factory.StartNew(() => 4 {
- 5 //复杂操作,等待10s
- 6Thread.Sleep(10000);
- 7
- 8}).ContinueWith((t) => 9 {
- 10button1.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 voidMain(string[] args)
- 6 {
- 7 vartask = Task.Factory.StartNew(() => 8 {
- 9Console.WriteLine("hello world!!!");
- 10},newCancellationToken(), 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 overrideIEnumerable GetScheduledTasks()
- 22 {
- 23 return null;
- 24 }
- 25
- 26 protected override void QueueTask(Task task)
- 27 {
- 28 varthread =newThread(() =>29 {
- 30 TryExecuteTask(task);
- 31 });
- 32
- 33 thread.Start();
- 34 }
- 35
- 36 protected override boolTryExecuteTaskInline(Task task,bool taskWasPreviouslyQueued)
- 37 {
- 38 throw new NotImplementedException();
- 39 }
- 40 }
- 41}
看到没有,自定义 Task 就是这么简单,其实自定义操作中最重要的就是其中的 QueueTask 方法,接下来我可以用 windbg 观察一下,确实是工作线程,而不是
线程池,没骗你~~~
好了,本篇就说到这里,希望对你有帮助。
来源: http://www.cnblogs.com/huangxincheng/p/6781581.html