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. winform设置文本框宽度 根据文字数量和字体返回宽度

    _LinkLabel.Width = TextRenderer.MeasureText(_LinkLabel.Text, _LinkLabel.Font).Width;

  2. org.apache.jasper.JasperException:省略"/html/sysmaintain/authority/user/../../module/verify_login.jsp" not found

    说明了JSP页面里引用安全登录页面的jsp路径代码:<%@ include file="../../module/verify_login.jsp"%>这句代码引用的路 ...

  3. 如何解决audiodg占用内存高(停止与重启audiodg服务)

    window7电脑audiodg.exe进程占用内存很高 首先想到的办法是结束该进程,于是在任务管理器里结束进程后,内存是释放了,但是发现发现电脑没有声音 去到电脑的system32目录下双击audi ...

  4. Web应用程序的自动化测试库-FluentAutomation

    FluentAutomation是流畅的自动化应用编程接口,支持Selenium和WatiN 连同它们所有的风格和驱动程序.自从Fluient支持Selenium,那就意味着你可以使用Selenium ...

  5. 解读ASP.NET 5 & MVC6系列

    本系列的大部分内容来自于微软源码的阅读和网络,大部分测试代码都是基于VS RC版本进行测试的. 解读ASP.NET 5 & MVC6系列(1):ASP.NET 5简介 解读ASP.NET 5 ...

  6. 【Android环境搭建】解决安装使用SDK Manager更新时的问题

    问题描述: 安装使用SDK Manager更新时出现问题 Failed to fetch URL  https://dl-ssl.google.com/android/repository/repos ...

  7. SqlServer2008到期升级企业版 密钥+图解

    最近使用SQL Server2008,结果Sql Server Management Studio提示过期了,如图: 遇到如上图情况,需要将SQL Server2008升级维护下,还是输入原来的密钥就 ...

  8. Spark读取HBase

    背景:公司有些业务需求是存储在HBase上的,总是有业务人员找我要各种数据,所以想直接用Spark( shell) 加载到RDD进行计算 摘要: 1.相关环境 2.代码例子 内容 1.相关环境 Spa ...

  9. python 邮件基础篇

    python 操作邮件,不是很方便,说实话还不是理解的特别透彻,这次想把自己碰到的东西总结下来 邮件有imap,pop,imap协议,这次使用的是imap4协议,主要用了imap4和mail类, 代码 ...

  10. c#方法

    1.引用型参数: 关键字:ref 2.输出型参数 关键字:out 例: double area(out double p) { double t=3.14*10; p=2*t*3.14; return ...