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)的更多相关文章
- 【Spark Core】任务运行机制和Task源代码浅析1
引言 上一小节<TaskScheduler源代码与任务提交原理浅析2>介绍了Driver側将Stage进行划分.依据Executor闲置情况分发任务,终于通过DriverActor向exe ...
- C# Task 源代码阅读(1)
平时我们开发中,经常使用Task,后续的.net版本种很多都和Task有关,比如asyn,await有了Task 我们很少就去关注Thread 了.Task 给我们带来了很多的便利之处.是我们更少的去 ...
- C# Task 源代码阅读(2)
上篇已经讲到Task 的默认的TaskScheduler 为ThreadPoolTaskScheduler. 这时我们回到原来的task 的start方法,在代码最后,调用了 ScheduleAndS ...
- celery(一) application
Application application celery在使用之前,必须首先实例化.e.g. app = Celery() app 是线程安全的,即:不同配置.组件和任务的多个app可以共存在同一 ...
- Spark源代码分析之六:Task调度(二)
话说在<Spark源代码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这种方法针对接收到的ReviveOffe ...
- Spark技术内幕: Task向Executor提交的源代码解析
在上文<Spark技术内幕:Stage划分及提交源代码分析>中,我们分析了Stage的生成和提交.可是Stage的提交,仅仅是DAGScheduler完毕了对DAG的划分,生成了一个计算拓 ...
- 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq
常量,字段,构造方法 常量 1.什么是常量 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...
- 自己开发一个 vsts agent 的 task
vsts 中支持自定义Build/Release的过程Task 目标:做一个可以读取 Xamarin.Android 所生成的 APK 的 基本信息的 task ,包括 package(包名) / a ...
- Spring 4.x Task 和 Schedule 概述(代java配置)
转载请注明https://zhangzhaoyu.github.io/2016/09/30/spring-task-and-schedule-deep-research/ 摘要 在很多业务场景中,系统 ...
随机推荐
- 项目架构开发:数据访问层之Cache
数据访问层简单介绍 数据访问层,提供整个项目的数据访问与持久化功能.在分层系统中所有有关数据访问.检索.持久化的任务,最终都将在这一层完成. 来看一个比较经典的数据访问层结构图 大概可以看出如下信息 ...
- lxd容器之GPU发现和加载
lxd gpu设备发现: // /dev/nvidia[0-9]+ type nvidiaGpuCards struct { path string major int minor int id st ...
- struts2中Action到底是什么,怎么理解
struts2中Action到底是什么,怎么理解 1.配置完web.xml2.创建视图页面login.jsp3.创建业务控制器LoginAction类(解释说:创建业务控制器LoginAction类, ...
- C#的for循环使用方法
for循环是程序语言开发中常见的技法之一,这类循环可以执行指定的次数,并维护它自己的计数器,要定义for循环,需要下述信息:1.初始化计数器变量的一个起始值;2.继续循环的条件,它应涉及到计数器变量; ...
- Jmeter+Badboy实战经验二(使用jmeter)
1. 新建线程组: TestPlan--添加--Threads(Users)--线程组
- JS入门(二)
关于运算符: js中判断运算符跟数学的运算符基本差不多,就是大于>,小于<,大于等于>=,小于等于<=,等于==,不等于!=,全等于===:可以看出来,跟我们印象中的判断运算符 ...
- Prince2是怎么考试的?想了解P2
自己在项目管理培训的行业已经有了5年的时间,经历了很多的学员和企业,和他们交流的问题,话题也很多. 在前几年,对于项目经理来讲关注的很多是单项目管理的工具技术模板,谈论最多的是,进度延期,成本超支,范 ...
- JQuery OOP 及 OOP思想的简易理解
在项目维护的时候,看到通篇的function实际上是非常费(痛)劲(苦),个人对于前端也不是特别熟悉,就想着JQuery能否也建立OOP的写法? 目的便于日后代码维护管理,就算不为了自己,日后交接后也 ...
- 队列工厂之RabbitMQ
本次和大家分享的是RabbitMQ队列的用法,前一篇文章队列工厂之(MSMQ)中在描述的时候已经搭建了简单工厂,因此本章内容是在其之上扩充的子项不再过多讲解工厂的代码了:RabbitMQ应该是现在互联 ...
- [lua] mac上如何编译snapshot(检测Lua中的内存泄露)
最近我们的unity手游频繁闪退,只要进入战斗场景,之后一段时间就会闪退,如果是在unity编辑器中则会报出not enough memory的错误!猜测应该是有内存泄漏: 由于我们使用了tolua, ...