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

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

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

一: ThreadPoolTaskScheduler

这种scheduler机制是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线程过多,上下文切换频繁,所以这种情况下让Task在Thread中执行还是非常不错的选择,当然如果你不指定这个LongRunning的话,那就

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

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

如果大家对windbg不熟悉的话,也没关系,先暂且不讨论,我们只要把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来执行,具体的也可以在这种scheduler中窥得一二。

        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(); }).ContinueWith((t) =>
{
button1.Text = "hello world";
}, TaskScheduler.FromCurrentSynchronizationContext());
}

三:自定义TaskScheduler 

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

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

求所有的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观察一下,确实是工作线程,而不是

线程池,没骗你~~~

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

用惯了Task,你应该也需要了解它的内部调度机制TaskScheduler的更多相关文章

  1. [.net 多线程]Task

    C# 异步编程Task整理(一) c# .Net并行和多线程编程之Task学习记录! .NET 实现并行的几种方式(一) Dispatcher介绍 [C#学习笔记]使用C#中的Dispatcher 用 ...

  2. 大话Spark(9)-源码之TaskScheduler

    上篇文章讲到DAGScheduler会把job划分为多个Stage,每个Stage中都会创建一批Task,然后把Task封装为TaskSet提交到TaskScheduler. 这里我们来一起看下Tas ...

  3. .Net多线程编程—任务Task

    1 System.Threading.Tasks.Task简介 一个Task表示一个异步操作,Task的创建和执行是独立的. 只读属性: 返回值 名称 说明 object AsyncState 表示在 ...

  4. .NET Task揭秘(一)

    Task为.NET提供了基于任务的异步模式,它不是线程,它运行在线程池的线程上.本着开源的精神, 本文以解读基于.NET4.5 Task源码的方式来揭秘Task的实现原理.   Task的创建 Tas ...

  5. 实现基于Task的异步模式

    返回该系列目录<基于Task的异步模式--全面介绍> 生成方法 编译器生成 在.NET Framework 4.5中,C#编译器实现了TAP.任何标有async关键字的方法都是异步方法,编 ...

  6. 【C#】线程之Task

    Task开启线程 有两种启动方式: 1.构造创建线程,然后启动 var taskForAction = new Task(() => { //do something }); taskForAc ...

  7. MapReduce作业的map task和reduce task调度参数

    MapReduce作业可以细分为map task和reduce task,而MRAppMaster又将map task和reduce task分为四种状态: 1.pending:刚启动但尚未向reso ...

  8. 【Hadoop代码笔记】Hadoop作业提交之TaskTracker获取Task

    一.概要描述 在上上一篇博文和上一篇博文中分别描述了jobTracker和其服务(功能)模块初始化完成后,接收JobClient提交的作业,并进行初始化.本文着重描述,JobTracker如何选择作业 ...

  9. Task的运行过程分析

    Task的运行过程分析 Task的运行通过Worker启动时生成的Executor实例进行, caseRegisteredExecutor(sparkProperties)=> logInfo( ...

随机推荐

  1. 非服务器的定期校正时间 Anacron

    与服务器不同,编程和办公用计算机不是连续24小时运行的.开关机的时间不固定,类似较时这样的任务无法保证运行. 对于这类机器,可以考虑使用 Anacron 进行设置. 在 Archlinux 中, An ...

  2. Cesium原理篇:3D Tiles(2)数据结构

    上一节介绍3D Tiles渲染调度的时候,我们提到目前Cesium支持的Cesium3DTileContent目前支持如下类型: Batched3DModel3DTileContent Instanc ...

  3. Python自动生产表情包

    作为一个数据分析师,应该信奉一句话--"一图胜千言".不过这里要说的并不是数据可视化,而是一款全民向的产品形态--表情包!!!! 表情包不仅仅是一种符号,更是一种文化--是促进社交 ...

  4. IntelliJ Idea和IntelliJ webstrm 常用快捷键

    Ctrl+Shift + Enter,语句完成"!",否定完成,输入表达式时按 "!"键Ctrl+E,最近的文件Ctrl+Shift+E,最近更改的文件Shif ...

  5. React中父组件与子组件之间的数据传递和标准化的思考

    React中父组件与子组件之间的数据传递的的实现大家都可以轻易做到,但对比很多人的实现方法,总是会有或多或少的差异.在一个团队中,这种实现的差异体现了每个人各自的理解的不同,但是反过来思考,一个团队用 ...

  6. self-question

    需要即刻提升的几大能力: 1.重装系统和安装各种软件 2.自学能力,多从实际案例中总结,多归纳反思 3.拓展人际关系,和别人沟通交流 4.遇到困难迎刃而上,而不是回避退缩

  7. golang RWMutex读写锁分析

    RWMutex:是基于Mutex实现的读写互斥锁,一个goroutine可以持有多个读锁或者一个写锁,同一时刻只能持有读锁或者写锁 数据结构设计: type RWMutex struct { w Mu ...

  8. 用js控制css属性

    在用js控制css属性时,行内css属性可以任意控制,但若是在<style></style>中写的css属性,均不能用alert读取,但是赋值却有几种现象, 第一种:无法读取, ...

  9. iOS开发之UIPopoverController

    1.概述 是iPad开发中常见的一种控制器(在iPhone上不允许使用),跟其他控制器不一样的是,它直接继承自NSObject,并非继承自UIViewController,它只占用部分屏幕空间来呈现信 ...

  10. iOS开发之数据存储之Core Data

    1.概述 Core Data框架提供了对象-关系映射(ORM)的功能,即能够将OC对象转化成数据,保存在SQLite3数据库文件中,也能够将保存在数据库中的数据还原成OC对象.在此数据操作期间,不需要 ...