Task为.NET提供了基于任务的异步模式,它不是线程,它运行在线程池的线程上。本着开源的精神, 本文以解读基于.NET4.5 Task源码的方式来揭秘Task的实现原理。
 
Task的创建
Task的创建方式主要有2种:Task.RunTask.Factory.StartNew,各自有不同的overload,这里只解读其中的一种方式,其他有兴趣的请自行解读。
先来看看Task.Run源码:
 public static Task Run(Action action, CancellationToken cancellationToken)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
return Task.InternalStartNew((Task) null, (Delegate) action, (object) null, cancellationToken, TaskScheduler.Default, TaskCreationOptions.DenyChildAttach, InternalTaskOptions.None, ref stackMark);
}
调用了Task.InternalStartNew,第一个参数为null,并传入TaskScheduler.DefaultTaskCreationOptions.DenyChildAttach.
再来看看Task.Factory.StartNew源码:
 public Task StartNew(Action<object> action, object state, CancellationToken cancellationToken)
{
StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
Task internalCurrent = Task.InternalCurrent;
return Task.InternalStartNew(internalCurrent, (Delegate) action, state, cancellationToken, this.GetDefaultScheduler(internalCurrent), this.m_defaultCreationOptions, InternalTaskOptions.None, ref stackMark);
}
也是调用Task.InternalStartNew,第一个参数为internalCurrent,当前为null,并传入GetDefaultScheduler(internalCurrent)m_defaultCreationOptions
 private TaskScheduler GetDefaultScheduler(Task currTask)
{
if (this.m_defaultScheduler != null)
return this.m_defaultScheduler;
if (currTask != null && (currTask.CreationOptions & TaskCreationOptions.HideScheduler) == TaskCreationOptions.None)
return currTask.ExecutingTaskScheduler;
return TaskScheduler.Default;
}
如果internalCurrent不为空而且options是TaskCreationOptions.HideScheduler,那么启用internalCurrent的TaskScheduler。可惜internalCurrent为null,所以启用默认的TaskScheduler,跟入代码发现默认的TaskScheduler是ThreadPoolTaskScheduler,看名字就知道用的是线程池的任务调度,跟“黑盒”传说的一样的。m_defaultCreationOptions在Task.Factory的默认无参构造函数里被赋值TaskCreationOptions.None
 public abstract class TaskScheduler
{
private static readonly ConditionalWeakTable<TaskScheduler, object> s_activeTaskSchedulers = new ConditionalWeakTable<TaskScheduler, object>();
private static readonly TaskScheduler s_defaultTaskScheduler = (TaskScheduler) new ThreadPoolTaskScheduler();
...
}
目前来看两个方法最大的区别在于TaskCreationOption的不同,一个是DenyChildAttach,另一个是None
接着往下看InternalStartNew
 internal static Task InternalStartNew(Task creatingTask, Delegate action, object state, CancellationToken cancellationToken, TaskScheduler scheduler, TaskCreationOptions options, InternalTaskOptions internalOptions, ref StackCrawlMark stackMark)
{
if (scheduler == null)
throw new ArgumentNullException("scheduler");
Task task = new Task(action, state, creatingTask, cancellationToken, options, internalOptions | InternalTaskOptions.QueuedByRuntime, scheduler);
task.PossiblyCaptureContext(ref stackMark);
task.ScheduleAndStart(false);
return task;
}
首先实例化一个Task:
 internal Task(Delegate action, object state, Task parent, CancellationToken cancellationToken, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
{
if (action == null)
throw new ArgumentNullException("action");
if ((creationOptions & TaskCreationOptions.AttachedToParent) != TaskCreationOptions.None || (internalOptions & InternalTaskOptions.SelfReplicating) != InternalTaskOptions.None)
this.m_parent = parent;
this.TaskConstructorCore((object) action, state, cancellationToken, creationOptions, internalOptions, scheduler);
}
如果option是AttachToParent,那么internalCurrent就赋值给m_parent,目前为null,SelfReplicating是用来做并行计算的,会在TPL里详解。随后调用TaskConstructorCore
 internal void TaskConstructorCore(object action, object state, CancellationToken cancellationToken, TaskCreationOptions creationOptions, InternalTaskOptions internalOptions, TaskScheduler scheduler)
{
this.m_action = action;
this.m_stateObject = state;
this.m_taskScheduler = scheduler;
if ((creationOptions & ~(TaskCreationOptions.PreferFairness | TaskCreationOptions.LongRunning | TaskCreationOptions.AttachedToParent | TaskCreationOptions.DenyChildAttach | TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously)) != TaskCreationOptions.None)
throw new ArgumentOutOfRangeException("creationOptions");
if ((creationOptions & TaskCreationOptions.LongRunning) != TaskCreationOptions.None && (internalOptions & InternalTaskOptions.SelfReplicating) != InternalTaskOptions.None)
throw new InvalidOperationException(Environment.GetResourceString("Task_ctor_LRandSR"));
int num = (int) (creationOptions | (TaskCreationOptions) internalOptions);
if (this.m_action == null || (internalOptions & InternalTaskOptions.ContinuationTask) != InternalTaskOptions.None)
num |= ;
this.m_stateFlags = num;
if (this.m_parent != null && (creationOptions & TaskCreationOptions.AttachedToParent) != TaskCreationOptions.None && (this.m_parent.CreationOptions & TaskCreationOptions.DenyChildAttach) == TaskCreationOptions.None)
this.m_parent.AddNewChild();
if (!cancellationToken.CanBeCanceled)
return;
this.AssignCancellationToken(cancellationToken, (Task) null, (TaskContinuation) null);
}
如果options不为DenyChildAttach而且m_parent不为空,则把当前task作为child添加到m_parent。也就是说Task.Run不允许把要执行的task作为当前task的child。
Task已创建,接着调用PossiblyCaptureContext来获取execution context。
 internal static ExecutionContext Capture(ref StackCrawlMark stackMark, ExecutionContext.CaptureOptions options)
{
ExecutionContext.Reader executionContextReader = Thread.CurrentThread.GetExecutionContextReader();
if (executionContextReader.IsFlowSuppressed)
return (ExecutionContext) null;
SecurityContext securityContext = SecurityContext.Capture(executionContextReader, ref stackMark);
HostExecutionContext executionContext1 = HostExecutionContextManager.CaptureHostExecutionContext();
SynchronizationContext synchronizationContext = (SynchronizationContext) null;
LogicalCallContext logicalCallContext = (LogicalCallContext) null;
if (!executionContextReader.IsNull)
{
if ((options & ExecutionContext.CaptureOptions.IgnoreSyncCtx) == ExecutionContext.CaptureOptions.None)
synchronizationContext = executionContextReader.SynchronizationContext == null ? (SynchronizationContext) null : executionContextReader.SynchronizationContext.CreateCopy();
if (executionContextReader.LogicalCallContext.HasInfo)
logicalCallContext = executionContextReader.LogicalCallContext.Clone();
}
Dictionary<IAsyncLocal, object> dictionary = (Dictionary<IAsyncLocal, object>) null;
List<IAsyncLocal> asyncLocalList = (List<IAsyncLocal>) null;
if (!executionContextReader.IsNull)
{
dictionary = executionContextReader.DangerousGetRawExecutionContext()._localValues;
asyncLocalList = executionContextReader.DangerousGetRawExecutionContext()._localChangeNotifications;
}
if ((options & ExecutionContext.CaptureOptions.OptimizeDefaultCase) != ExecutionContext.CaptureOptions.None && securityContext == null && (executionContext1 == null && synchronizationContext == null) && ((logicalCallContext == null || !logicalCallContext.HasInfo) && (dictionary == null && asyncLocalList == null)))
return ExecutionContext.s_dummyDefaultEC;
ExecutionContext executionContext2 = new ExecutionContext();
executionContext2.SecurityContext = securityContext;
if (executionContext2.SecurityContext != null)
executionContext2.SecurityContext.ExecutionContext = executionContext2;
executionContext2._hostExecutionContext = executionContext1;
executionContext2._syncContext = synchronizationContext;
executionContext2.LogicalCallContext = logicalCallContext;
executionContext2._localValues = dictionary;
executionContext2._localChangeNotifications = asyncLocalList;
executionContext2.isNewCapture = true;
return executionContext2;
}
ExecutionContext包含了SecurityContext,SynchronizationContext以及LogicalCallContext,其中SynchronizationContext需要做CreateCopy,LogicalCallContext需要做clone,所有这一切都是用户态的,不涉及内核,性能棒棒哒!
接着调用ScheduleAndStart:
 internal void ScheduleAndStart(bool needsProtection)
{
if (needsProtection)
{
if (!this.MarkStarted())
return;
}
else
this.m_stateFlags = this.m_stateFlags | ;
if (Task.s_asyncDebuggingEnabled)
Task.AddToActiveTasks(this);
if (AsyncCausalityTracer.LoggingOn && (this.Options & (TaskCreationOptions) ) == TaskCreationOptions.None)
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: " + ((Delegate) this.m_action).Method.Name, 0UL);
try
{
this.m_taskScheduler.InternalQueueTask(this);
}
catch (ThreadAbortException ex)
{
this.AddException((object) ex);
this.FinishThreadAbortedTask(true, false);
}
catch (System.Exception ex)
{
TaskSchedulerException schedulerException = new TaskSchedulerException(ex);
this.AddException((object) schedulerException);
this.Finish(false);
if ((this.Options & (TaskCreationOptions) ) == TaskCreationOptions.None)
this.m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
throw schedulerException;
}
} internal void InternalQueueTask(Task task)
{
task.FireTaskScheduledIfNeeded(this);
this.QueueTask(task);
}
FireTaskScheduledIfNeeded判断是否开启EWT Trace,接着调用ThreadPoolTaskScheduler.QueueTask
 private static readonly ParameterizedThreadStart s_longRunningThreadWork = new ParameterizedThreadStart(ThreadPoolTaskScheduler.LongRunningThreadWork);
private static void LongRunningThreadWork(object obj)
{
(obj as Task).ExecuteEntry(false);
}
protected internal override void QueueTask(Task task)
{
if ((task.Options & TaskCreationOptions.LongRunning) != TaskCreationOptions.None)
{
new Thread(ThreadPoolTaskScheduler.s_longRunningThreadWork)
{
IsBackground = true
}.Start((object) task);
}
else
{
bool forceGlobal = (uint) (task.Options & TaskCreationOptions.PreferFairness) > 0U;
ThreadPool.UnsafeQueueCustomWorkItem((IThreadPoolWorkItem) task, forceGlobal);
}
}
如果options是LongRunning,那么单独创建一个线程执行该任务(ExecuteEntry),否则就调用ThreadPool.UnsafeQueueCustomWorkItem,这个方法我们熟,还记得在.net线程池内幕里有讲到的global work queue和local work queue吗?给ThreadPool添加一个任务实际上是在global work queue添加一个任务,而task就是往local work queue里添加任务。
ThreadPoolWorkQueue源码:
 public void Enqueue(IThreadPoolWorkItem callback, bool forceGlobal)
{
ThreadPoolWorkQueueThreadLocals queueThreadLocals = (ThreadPoolWorkQueueThreadLocals) null;
if (!forceGlobal)
queueThreadLocals = ThreadPoolWorkQueueThreadLocals.threadLocals;
if (this.loggingEnabled)
FrameworkEventSource.Log.ThreadPoolEnqueueWorkObject((object) callback);
if (queueThreadLocals != null)
{
queueThreadLocals.workStealingQueue.LocalPush(callback);
}
else
{
ThreadPoolWorkQueue.QueueSegment comparand = this.queueHead;
while (!comparand.TryEnqueue(callback))
{
Interlocked.CompareExchange<ThreadPoolWorkQueue.QueueSegment>(ref comparand.Next, new ThreadPoolWorkQueue.QueueSegment(), (ThreadPoolWorkQueue.QueueSegment) null);
for (; comparand.Next != null; comparand = this.queueHead)
Interlocked.CompareExchange<ThreadPoolWorkQueue.QueueSegment>(ref this.queueHead, comparand.Next, comparand);
}
}
this.EnsureThreadRequested();
}
由于线程已经执行过任务(global的也有可能是local的),所以代码会走到queueThreadLocals.workStealingQueue.LocalPush(callback)
 internal volatile IThreadPoolWorkItem[] m_array = new IThreadPoolWorkItem[];
private SpinLock m_foreignLock = new SpinLock(false);
public void LocalPush(IThreadPoolWorkItem obj)
{
int num1 = this.m_tailIndex;
if (num1 == int.MaxValue)
{
bool lockTaken = false;
try
{
this.m_foreignLock.Enter(ref lockTaken);
if (this.m_tailIndex == int.MaxValue)
{
this.m_headIndex = this.m_headIndex & this.m_mask;
this.m_tailIndex = num1 = this.m_tailIndex & this.m_mask;
}
}
finally
{
if (lockTaken)
this.m_foreignLock.Exit(true);
}
}
if (num1 < this.m_headIndex + this.m_mask)
{
Volatile.Write<IThreadPoolWorkItem>(ref this.m_array[num1 & this.m_mask], obj);
this.m_tailIndex = num1 + ;
}
else
{
bool lockTaken = false;
try
{
this.m_foreignLock.Enter(ref lockTaken);
int num2 = this.m_headIndex;
int num3 = this.m_tailIndex - this.m_headIndex;
if (num3 >= this.m_mask)
{
IThreadPoolWorkItem[] threadPoolWorkItemArray = new IThreadPoolWorkItem[this.m_array.Length << ];
for (int index = ; index < this.m_array.Length; ++index)
threadPoolWorkItemArray[index] = this.m_array[index + num2 & this.m_mask];
this.m_array = threadPoolWorkItemArray;
this.m_headIndex = ;
this.m_tailIndex = num1 = num3;
this.m_mask = this.m_mask << | ;
}
Volatile.Write<IThreadPoolWorkItem>(ref this.m_array[num1 & this.m_mask], obj);
this.m_tailIndex = num1 + ;
}
finally
{
if (lockTaken)
this.m_foreignLock.Exit(false);
}
}
}
Local work queue(m_array)首先被限死为32,如果queue超过最大数了,则扩大为原来的2倍,以此类推。这里也使用了自旋锁和内存写屏障来代替同步锁提高性能。
 
至此,task已被创建好,并加入到了ThreadPool的local work queue。那么task是如何被调度的呢?为什么LongRunning就要单独起一个线程去做?请听下回分解!

.NET Task揭秘(一)的更多相关文章

  1. 学习ASP.NET Web API框架揭秘之“HTTP方法重写”

    最近在看老A的<ASP.NET Web API 框架揭秘>,这本书对于本人现阶段来说还是比较合适的(对于调用已经较为熟悉,用其开发过项目,但未深入理解过很多内容为何可以这样“调用”).看到 ...

  2. Spark Tungsten揭秘 Day3 内存分配和管理内幕

    Spark Tungsten揭秘 Day3 内存分配和管理内幕 恭喜Spark2.0发布,今天会看一下2.0的源码. 今天会讲下Tungsten内存分配和管理的内幕.Tungsten想要工作,要有数据 ...

  3. Spark Streaming揭秘 Day30 集群模式下SparkStreaming日志分析

    Spark Streaming揭秘 Day30 集群模式下SparkStreaming日志分析 今天通过集群运行模式观察.研究和透彻的刨析SparkStreaming的日志和web监控台. Day28 ...

  4. Spark Streaming揭秘 Day17 资源动态分配

    Spark Streaming揭秘 Day17 资源动态分配 今天,让我们研究一下一个在Spark中非常重要的特性:资源动态分配. 为什么要动态分配?于Spark不断运行,对资源也有不小的消耗,在默认 ...

  5. Spark Streaming揭秘 Day4-事务一致性(Exactly one)

    Spark Streaming揭秘 Day4 事务一致性Exactly one 引子 对于业务处理系统,事务的一致性非常的关键,事务一致性(Exactly one),简单来说,就是输入数据一定会被处理 ...

  6. ASP.NET Web API框架揭秘:路由系统的几个核心类型

    ASP.NET Web API框架揭秘:路由系统的几个核心类型 虽然ASP.NET Web API框架采用与ASP.NET MVC框架类似的管道式设计,但是ASP.NET Web API管道的核心部分 ...

  7. 第四节:Task的启动的四种方式以及Task、TaskFactory的线程等待和线程延续的解决方案

    一. 背景 揭秘: 在前面的章节介绍过,Task出现之前,微软的多线程处理方式有:Thread→ThreadPool→委托的异步调用,虽然也可以基本业务需要的多线程场景,但它们在多个线程的等待处理方面 ...

  8. 第五节:Task构造函数之TaskCreationOptions枚举处理父子线程之间的关系。

    一. 整体说明 揭秘: 通过F12查看Task类的源码(详见下面的截图),发现Task类的构造函数有有一个参数为:TaskCreationOptions类型,本章节可以算作是一个扩展章节,主要就来研究 ...

  9. 第六节:深入研究Task实例方法ContinueWith的参数TaskContinuationOptions

    一. 整体说明 揭秘: 该章节的性质和上一个章节类似,也是一个扩展的章节,主要来研究Task类下的实例方法ContinueWith中的参数TaskContinuationOptions. 通过F12查 ...

随机推荐

  1. ASP.NET SignalR 高可用设计

    在 One ASP.NET 的架构图中,微软将 WebAPI 和 SignalR 归类到 Services 类型与 MVC.Web Forms 同列为一等公民,未来的 ASP.NET 5 尽管还在be ...

  2. OOAD利器之UML基础

    UML:Unified Modeling Language,即统一建模语言,简单地说就是一种有特殊用处的语言.本文是我初步学习UML的学习笔记,对于我们菜鸟码农来说,让我们做设计的可能性不大,但至少能 ...

  3. 【Java并发编程实战】-----“J.U.C”:ReentrantLock之二lock方法分析

    前一篇博客简单介绍了ReentrantLock的定义和与synchronized的区别,下面跟随LZ的笔记来扒扒ReentrantLock的lock方法.我们知道ReentrantLock有公平锁.非 ...

  4. ASP.NET MVC 路由(三)

    ASP.NET MVC路由(三) 前言 通过前两篇的学习会对路由系统会有一个初步的了解,并且对路由系统中的Url规则有个简单的了解,在大家的脑海中也有个印象了,那么路由系统在ASP.NETMVC中所处 ...

  5. 用JavaScript调用WCF Service

    原创地址:http://www.cnblogs.com/jfzhu/p/4039604.html 转载请注明出处 前面介绍过<Step by Step 创建一个WCF Service>和& ...

  6. 使用HTML5的History API

    HTML5 History API提供了一种功能,能让开发人员在不刷新整个页面的情况下修改站点的URL.这个功能很有用,例如通过一段JavaScript代码局部加载页面的内容,你希望通过改变当前页面的 ...

  7. [译]如何防止elasticsearch的脑裂问题

    本文翻译自blog.trifork.com的博文 地址是http://blog.trifork.com/2013/10/24/how-to-avoid-the-split-brain-problem- ...

  8. Java中instanceof和isInstance区别详解

     一次性搞定instanceof和isInstance,instanceof和isInstance长的非常像,用法也很类似,先看看这两个的用法: obj.instanceof(class) 也就是说这 ...

  9. Atitit  补充说明 sql知识图谱与线路图attilax总结补充说明

    Atitit  补充说明 sql知识图谱与线路图attilax总结补充说明 1. 常见编程语言的分类  :命令式语言.函数式语言.逻辑语言1 1.1. 按照编程语言的代际划分,又2gl,3gl,4gl ...

  10. Android笔记——判断程序是否第一次启动

    public class Welcome extends Activity { private final long SPLASH_LENGTH = 2000; Handler handler = n ...