C# Task 源代码阅读(2)
上篇已经讲到Task 的默认的TaskScheduler 为ThreadPoolTaskScheduler.
这时我们回到原来的task 的start方法,在代码最后,调用了 ScheduleAndStart(true) 这个方法。接着看这个方法
[SecuritySafeCritical] // Needed for QueueTask
internal void ScheduleAndStart(bool needsProtection)
{
Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == , "task has already started"); // Set the TASK_STATE_STARTED bit
if (needsProtection)
{
if (!MarkStarted())
{
// A cancel has snuck in before we could get started. Quietly exit.
return;
}
}
else
{
m_stateFlags |= TASK_STATE_STARTED;
} if (s_asyncDebuggingEnabled)
{
AddToActiveTasks(this);
} if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == )
{
//For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: "+((Delegate)m_action).Method.Name, );
} try
{
// Queue to the indicated scheduler.
m_taskScheduler.InternalQueueTask(this);
}
catch (ThreadAbortException tae)
{
AddException(tae);
FinishThreadAbortedTask(true, false);
}
catch (Exception e)
{
// The scheduler had a problem queueing this task. Record the exception, leaving this task in
// a Faulted state.
TaskSchedulerException tse = new TaskSchedulerException(e);
AddException(tse);
Finish(false); // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
// or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
// to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
// and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer. if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == )
{
// m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
Contract.Assert(
(m_contingentProperties != null) &&
(m_contingentProperties.m_exceptionsHolder != null) &&
(m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
"Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
"and to have faults recorded."); m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
}
// re-throw the exception wrapped as a TaskSchedulerException.
throw tse;
}
}
开始先做契约参数认证,接着保护数值判断。我们要看的是AddToActiveTasks(this)这个方法,注意在他之前有个判断,在s_asyncDebuggingEnabled 为true 的情况才会执行,当然默认的是false。
// This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
// task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
[FriendAccessAllowed]
internal static bool AddToActiveTasks(Task task)
{
Contract.Requires(task != null, "Null Task objects can't be added to the ActiveTasks collection");
lock (s_activeTasksLock)
{
s_currentActiveTasks[task.Id] = task;
}
//always return true to keep signature as bool for backwards compatibility
return true;
}
这个就是僵我们要执行task 对象放入一个字典中,放入的目的是做什么呢?当然就是为何方便查询和管理。这个方法在正常流程是不会执行的。这里觉得有些奇怪的写法,Task 类里面有个静态静态字典,用于存放自己执行的类集合。当然说到管理和查询,断然我是不会放在这个类,令起新类也好。
这里的代码方法参数验证都是采用契约验证,其实我个人并不是很赞同这东西,虽然C++也有这个。我倒更希望是原本的异常抛出,或者日志记录,或者其他自定义方式。
接着看核心方法 m_taskScheduler.InternalQueueTask(this); 前面我们已经看到默认的m_taskScheduler为ThreadPoolTaskScheduler。接着看代码
[SecurityCritical]
internal void InternalQueueTask(Task task)
{
Contract.Requires(task != null); task.FireTaskScheduledIfNeeded(this); this.QueueTask(task);
}
[SecurityCritical]
protected internal override void QueueTask(Task task)
{
if ((task.Options & TaskCreationOptions.LongRunning) != )
{
// Run LongRunning tasks on their own dedicated thread.
Thread thread = new Thread(s_longRunningThreadWork);
thread.IsBackground = true; // Keep this thread from blocking process shutdown
thread.Start(task);
}
else
{
// Normal handling for non-LongRunning tasks.
bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != );
ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);
}
}
现在为止就开始清晰明朗了,看到QueueTask 方法,我已经可以看到task 对象已经传到Threadpool 里面了。至此,可以说到task 一般都是在ThreadPool 里面运行。接着我们再看ThreadpoolTaskScheduler让几个重要的方法
[SecurityCritical]
protected internal override bool TryDequeue(Task task)
{
// just delegate to TP
return ThreadPool.TryPopCustomWorkItem(task);
} [SecurityCritical]
protected override IEnumerable<Task> GetScheduledTasks()
{
return FilterTasksFromWorkItems(ThreadPool.GetQueuedWorkItems());
} /// <summary>
/// This internal function will do this:
/// (1) If the task had previously been queued, attempt to pop it and return false if that fails.
/// (2) Propagate the return value from Task.ExecuteEntry() back to the caller.
///
/// IMPORTANT NOTE: TryExecuteTaskInline will NOT throw task exceptions itself. Any wait code path using this function needs
/// to account for exceptions that need to be propagated, and throw themselves accordingly.
/// </summary>
[SecurityCritical]
protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
{ -----------------------
}
这里有GetScheduledTasks()方法,这个方法就是用来获得当前的Task的,对于去珍断task 的运行状态非常有帮助。至此。我们一步一步看到Task 是如何运行的,当然到达Theadpool可以继续看下去。注意了ThreadPoolTaskScheduler 的访问修饰符是internal sealed,所以在用task 的时候无法用到他,还有里面的方法访问修饰符都是protected 的。到此,我们正常来运行task,还是没法获得到task的本身运行状态。很多人在代码中为了实现某个功能都会大量的使用task,每个人的写法有不一样,task 运行是否成功,是否发生异常 对于整个项目的运行至关重要。那么如何管理,如何查看task 的运行状态呢,在C# code 我们如果想把task 的异常接管到主线程种,必须task wait,但是很多task 都是无需直到返回结果,但是实际上我们还是要关心他的运行状态,那么如何来做,如何来看呢。
1.常规做法,鉴于很多人喜欢TaskFactory.StartNew() 这个写法,所以想把所有的task的加入到一个队列中比较麻烦,因为启动task 的写法很多。所以各自的task的里面自己处理异常,写好日志。
2.使用TaskScheduler,看代码的目的除了了解运行过程,更加了解如和使用这个类,我们只需要写上自己的TaskScheduler,当然继承这个类,是需要实现某些必须方法的,不管是task的start还是TaskFactory的StartNew方法,我们都可以注入自己的TaskScheduler,这样正如TaskScheduler设计初衷一样,所有的task 运行都会交给他来管理,默认的ThreadPoolTaskScheduler是没法使用的(访问修饰符),除非采用一些其他手段,这里不多介绍。所以只能自己重新去实现这个类的相关细节。
C# Task 源代码阅读(2)的更多相关文章
- C# Task 源代码阅读(1)
平时我们开发中,经常使用Task,后续的.net版本种很多都和Task有关,比如asyn,await有了Task 我们很少就去关注Thread 了.Task 给我们带来了很多的便利之处.是我们更少的去 ...
- 非常好!!!Linux源代码阅读——中断【转】
Linux源代码阅读——中断 转自:http://home.ustc.edu.cn/~boj/courses/linux_kernel/2_int.html 目录 为什么要有中断 中断的作用 中断的处 ...
- XV6源代码阅读-进程线程
Exercise1 源代码阅读 1.基本头文件:types.h param.h memlayout.h defs.h x86.h asm.h mmu.h elf.h types.h:仅仅是定义uint ...
- Mongodb源代码阅读笔记:Journal机制
Mongodb源代码阅读笔记:Journal机制 Mongodb源代码阅读笔记:Journal机制 涉及的文件 一些说明 PREPLOGBUFFER WRITETOJOURNAL WRITETODAT ...
- 【转】Tomcat总体结构(Tomcat源代码阅读系列之二)
本文是Tomcat源代码阅读系列的第二篇文章,我们在本系列的第一篇文章:在IntelliJ IDEA 和 Eclipse运行tomcat 7源代码一文中介绍了如何在intelliJ IDEA 和 Ec ...
- 利用doxygen提高源代码阅读效率
阅读开源项目的源代码是提高自己编程能力的好方法,而有一个好的源代码阅读工具无疑能够让你在阅读源代码时事半功倍.之前找过不少源代码阅读工具,像SourceInsight.sourcenav.scitoo ...
- CI框架源代码阅读笔记5 基准測试 BenchMark.php
上一篇博客(CI框架源代码阅读笔记4 引导文件CodeIgniter.php)中.我们已经看到:CI中核心流程的核心功能都是由不同的组件来完毕的.这些组件类似于一个一个单独的模块,不同的模块完毕不同的 ...
- 淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式
淘宝数据库OceanBase SQL编译器部分 源代码阅读--Schema模式 什么是Database,什么是Schema,什么是Table,什么是列,什么是行,什么是User?我们能够能够把Data ...
- CI框架源代码阅读笔记3 全局函数Common.php
从本篇開始.将深入CI框架的内部.一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说.全局函数具有最高的载入优先权.因此大多数的框架中BootStrap ...
随机推荐
- Lvs Keepalive DR模式高可用配置
Lvs Keepalive DR模式配置 一.环境 #DIP# eth0:192.168.233.145#VIP# eth0:0 192.168.233.250/32 #RIP1:192.168.23 ...
- Cookie的实现
Cookie是web server下发给浏览器的任意的一段文本,在后续的http 请求中,浏览器会将cookie带回给Web Server.同时在浏览器允许脚本执行的情况下,Cookie是可以被Jav ...
- C# 调用指定打印机 (并不是默认)
this.printDocument1.PrinterSettings.PrinterName = "Microsoft XPS Document Writer"; this.pr ...
- .net core里用ZXing生成二维码
先获取Nuget包 static void Main(string[] args) { string content = "二维码信息"; BitMatrix byteMatrix ...
- JAVA经典题--计算一个字符串中每个字符出现的次数
需求: 计算一个字符串中每个字符出现的次数 思路: 通过toCharArray()拿到一个字符数组--> 遍历数组,将数组元素作为key,数值1作为value存入map容器--> 如果k ...
- Ubuntu 16.04 安装R和RStudio
在Ubuntu上安装R和Rstudio的时候碰到了一些依赖项不存在的错误 The following packages have unmet dependencies: r-base-core : D ...
- Silverlight之我见——数据批示(1)
第一次听到这个概念,你是否有点陌生?MSDN上也没有特意的去说明.不要看到这个名词不太熟悉,其实数据批示,玩过C#的人都会非常熟悉,所谓数据批示,其本质就是特性(Attribute),怎么样,现在有点 ...
- 第十四节:pandas之merge()合并
Pandas提供了一个merge()函数,作为DataFrame对象之间所有标准数据库连接操作的入口pandas.merge(left,right,how='inner',on=None,left_o ...
- ssm 数据库连接池配置
1.工程引入druid-1.1.2.jar包2.修改spring-common.xml文件 <!-- 1. 数据源 : DruidDataSource--> <bean id=&qu ...
- Tkinter图形界面设计(GUI)
[因为这是我第一个接触的GUI图形界面python库,现在也不用了,所以大多数内容都来自之前花 钱买的一些快速入门的内容,可以当作简单的知识点查询使用] 在此声明:内容来自微信公众号GitChat,付 ...