上篇已经讲到Task 的默认的TaskScheduler 为ThreadPoolTaskScheduler.

这时我们回到原来的task 的start方法,在代码最后,调用了 ScheduleAndStart(true) 这个方法。接着看这个方法

  1. [SecuritySafeCritical] // Needed for QueueTask
  2. internal void ScheduleAndStart(bool needsProtection)
  3. {
  4. Contract.Assert(m_taskScheduler != null, "expected a task scheduler to have been selected");
  5. Contract.Assert((m_stateFlags & TASK_STATE_STARTED) == , "task has already started");
  6.  
  7. // Set the TASK_STATE_STARTED bit
  8. if (needsProtection)
  9. {
  10. if (!MarkStarted())
  11. {
  12. // A cancel has snuck in before we could get started. Quietly exit.
  13. return;
  14. }
  15. }
  16. else
  17. {
  18. m_stateFlags |= TASK_STATE_STARTED;
  19. }
  20.  
  21. if (s_asyncDebuggingEnabled)
  22. {
  23. AddToActiveTasks(this);
  24. }
  25.  
  26. if (AsyncCausalityTracer.LoggingOn && (Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == )
  27. {
  28. //For all other task than TaskContinuations we want to log. TaskContinuations log in their constructor
  29. AsyncCausalityTracer.TraceOperationCreation(CausalityTraceLevel.Required, this.Id, "Task: "+((Delegate)m_action).Method.Name, );
  30. }
  31.  
  32. try
  33. {
  34. // Queue to the indicated scheduler.
  35. m_taskScheduler.InternalQueueTask(this);
  36. }
  37. catch (ThreadAbortException tae)
  38. {
  39. AddException(tae);
  40. FinishThreadAbortedTask(true, false);
  41. }
  42. catch (Exception e)
  43. {
  44. // The scheduler had a problem queueing this task. Record the exception, leaving this task in
  45. // a Faulted state.
  46. TaskSchedulerException tse = new TaskSchedulerException(e);
  47. AddException(tse);
  48. Finish(false);
  49.  
  50. // Now we need to mark ourselves as "handled" to avoid crashing the finalizer thread if we are called from StartNew()
  51. // or from the self replicating logic, because in both cases the exception is either propagated outside directly, or added
  52. // to an enclosing parent. However we won't do this for continuation tasks, because in that case we internally eat the exception
  53. // and therefore we need to make sure the user does later observe it explicitly or see it on the finalizer.
  54.  
  55. if ((Options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) == )
  56. {
  57. // m_contingentProperties.m_exceptionsHolder *should* already exist after AddException()
  58. Contract.Assert(
  59. (m_contingentProperties != null) &&
  60. (m_contingentProperties.m_exceptionsHolder != null) &&
  61. (m_contingentProperties.m_exceptionsHolder.ContainsFaultList),
  62. "Task.ScheduleAndStart(): Expected m_contingentProperties.m_exceptionsHolder to exist " +
  63. "and to have faults recorded.");
  64.  
  65. m_contingentProperties.m_exceptionsHolder.MarkAsHandled(false);
  66. }
  67. // re-throw the exception wrapped as a TaskSchedulerException.
  68. throw tse;
  69. }
  70. }

开始先做契约参数认证,接着保护数值判断。我们要看的是AddToActiveTasks(this)这个方法,注意在他之前有个判断,在s_asyncDebuggingEnabled 为true 的情况才会执行,当然默认的是false。

  1. // This dictonary relates the task id, from an operation id located in the Async Causality log to the actual
  2. // task. This is to be used by the debugger ONLY. Task in this dictionary represent current active tasks.
  3. private static readonly Dictionary<int, Task> s_currentActiveTasks = new Dictionary<int, Task>();
  4. [FriendAccessAllowed]
  5. internal static bool AddToActiveTasks(Task task)
  6. {
  7. Contract.Requires(task != null, "Null Task objects can't be added to the ActiveTasks collection");
  8. lock (s_activeTasksLock)
  9. {
  10. s_currentActiveTasks[task.Id] = task;
  11. }
  12. //always return true to keep signature as bool for backwards compatibility
  13. return true;
  14. }

这个就是僵我们要执行task 对象放入一个字典中,放入的目的是做什么呢?当然就是为何方便查询和管理。这个方法在正常流程是不会执行的。这里觉得有些奇怪的写法,Task 类里面有个静态静态字典,用于存放自己执行的类集合。当然说到管理和查询,断然我是不会放在这个类,令起新类也好。

这里的代码方法参数验证都是采用契约验证,其实我个人并不是很赞同这东西,虽然C++也有这个。我倒更希望是原本的异常抛出,或者日志记录,或者其他自定义方式。

接着看核心方法 m_taskScheduler.InternalQueueTask(this); 前面我们已经看到默认的m_taskScheduler为ThreadPoolTaskScheduler。接着看代码

  1. [SecurityCritical]
  2. internal void InternalQueueTask(Task task)
  3. {
  4. Contract.Requires(task != null);
  5.  
  6. task.FireTaskScheduledIfNeeded(this);
  7.  
  8. this.QueueTask(task);
  9. }
  10. [SecurityCritical]
  11. protected internal override void QueueTask(Task task)
  12. {
  13. if ((task.Options & TaskCreationOptions.LongRunning) != )
  14. {
  15. // Run LongRunning tasks on their own dedicated thread.
  16. Thread thread = new Thread(s_longRunningThreadWork);
  17. thread.IsBackground = true; // Keep this thread from blocking process shutdown
  18. thread.Start(task);
  19. }
  20. else
  21. {
  22. // Normal handling for non-LongRunning tasks.
  23. bool forceToGlobalQueue = ((task.Options & TaskCreationOptions.PreferFairness) != );
  24. ThreadPool.UnsafeQueueCustomWorkItem(task, forceToGlobalQueue);
  25. }
  26. }

现在为止就开始清晰明朗了,看到QueueTask 方法,我已经可以看到task 对象已经传到Threadpool 里面了。至此,可以说到task 一般都是在ThreadPool 里面运行。接着我们再看ThreadpoolTaskScheduler让几个重要的方法

  1. [SecurityCritical]
  2. protected internal override bool TryDequeue(Task task)
  3. {
  4. // just delegate to TP
  5. return ThreadPool.TryPopCustomWorkItem(task);
  6. }
  7.  
  8. [SecurityCritical]
  9. protected override IEnumerable<Task> GetScheduledTasks()
  10. {
  11. return FilterTasksFromWorkItems(ThreadPool.GetQueuedWorkItems());
  12. }
  13.  
  14. /// <summary>
  15. /// This internal function will do this:
  16. /// (1) If the task had previously been queued, attempt to pop it and return false if that fails.
  17. /// (2) Propagate the return value from Task.ExecuteEntry() back to the caller.
  18. ///
  19. /// IMPORTANT NOTE: TryExecuteTaskInline will NOT throw task exceptions itself. Any wait code path using this function needs
  20. /// to account for exceptions that need to be propagated, and throw themselves accordingly.
  21. /// </summary>
  22. [SecurityCritical]
  23. protected override bool TryExecuteTaskInline(Task task, bool taskWasPreviouslyQueued)
  24. {
  25.  
  26. -----------------------
  27. }
  1. 这里有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,当然继承这个类,是需要实现某些必须方法的,不管是taskstart还是TaskFactoryStartNew方法,我们都可以注入自己的TaskScheduler,这样正如TaskScheduler设计初衷一样,所有的task 运行都会交给他来管理,默认的ThreadPoolTaskScheduler是没法使用的(访问修饰符),除非采用一些其他手段,这里不多介绍。所以只能自己重新去实现这个类的相关细节。

C# Task 源代码(2)的更多相关文章

  1. 【Spark Core】任务运行机制和Task源代码浅析1

    引言 上一小节<TaskScheduler源代码与任务提交原理浅析2>介绍了Driver側将Stage进行划分.依据Executor闲置情况分发任务,终于通过DriverActor向exe ...

  2. C# Task 源代码阅读(1)

    平时我们开发中,经常使用Task,后续的.net版本种很多都和Task有关,比如asyn,await有了Task 我们很少就去关注Thread 了.Task 给我们带来了很多的便利之处.是我们更少的去 ...

  3. C# Task 源代码阅读(2)

    上篇已经讲到Task 的默认的TaskScheduler 为ThreadPoolTaskScheduler. 这时我们回到原来的task 的start方法,在代码最后,调用了 ScheduleAndS ...

  4. celery(一) application

    Application application celery在使用之前,必须首先实例化.e.g. app = Celery() app 是线程安全的,即:不同配置.组件和任务的多个app可以共存在同一 ...

  5. Spark源代码分析之六:Task调度(二)

    话说在<Spark源代码分析之五:Task调度(一)>一文中,我们对Task调度分析到了DriverEndpoint的makeOffers()方法.这种方法针对接收到的ReviveOffe ...

  6. Spark技术内幕: Task向Executor提交的源代码解析

    在上文<Spark技术内幕:Stage划分及提交源代码分析>中,我们分析了Stage的生成和提交.可是Stage的提交,仅仅是DAGScheduler完毕了对DAG的划分,生成了一个计算拓 ...

  7. 常量,字段,构造方法 调试 ms 源代码 一个C#二维码图片识别的Demo 近期ASP.NET问题汇总及对应的解决办法 c# chart控件柱状图,改变柱子宽度 使用C#创建Windows服务 C#服务端判断客户端socket是否已断开的方法 线程 线程池 Task .NET 单元测试的利剑——模拟框架Moq

    常量,字段,构造方法   常量 1.什么是常量 ​ 常量是值从不变化的符号,在编译之前值就必须确定.编译后,常量值会保存到程序集元数据中.所以,常量必须是编译器识别的基元类型的常量,如:Boolean ...

  8. 自己开发一个 vsts agent 的 task

    vsts 中支持自定义Build/Release的过程Task 目标:做一个可以读取 Xamarin.Android 所生成的 APK 的 基本信息的 task ,包括 package(包名) / a ...

  9. Spring 4.x Task 和 Schedule 概述(代java配置)

    转载请注明https://zhangzhaoyu.github.io/2016/09/30/spring-task-and-schedule-deep-research/ 摘要 在很多业务场景中,系统 ...

随机推荐

  1. 项目架构开发:数据访问层之Cache

    数据访问层简单介绍 数据访问层,提供整个项目的数据访问与持久化功能.在分层系统中所有有关数据访问.检索.持久化的任务,最终都将在这一层完成. 来看一个比较经典的数据访问层结构图 大概可以看出如下信息 ...

  2. lxd容器之GPU发现和加载

    lxd gpu设备发现: // /dev/nvidia[0-9]+ type nvidiaGpuCards struct { path string major int minor int id st ...

  3. struts2中Action到底是什么,怎么理解

    struts2中Action到底是什么,怎么理解 1.配置完web.xml2.创建视图页面login.jsp3.创建业务控制器LoginAction类(解释说:创建业务控制器LoginAction类, ...

  4. C#的for循环使用方法

    for循环是程序语言开发中常见的技法之一,这类循环可以执行指定的次数,并维护它自己的计数器,要定义for循环,需要下述信息:1.初始化计数器变量的一个起始值;2.继续循环的条件,它应涉及到计数器变量; ...

  5. Jmeter+Badboy实战经验二(使用jmeter)

    1. 新建线程组: TestPlan--添加--Threads(Users)--线程组

  6. JS入门(二)

    关于运算符: js中判断运算符跟数学的运算符基本差不多,就是大于>,小于<,大于等于>=,小于等于<=,等于==,不等于!=,全等于===:可以看出来,跟我们印象中的判断运算符 ...

  7. Prince2是怎么考试的?想了解P2

    自己在项目管理培训的行业已经有了5年的时间,经历了很多的学员和企业,和他们交流的问题,话题也很多. 在前几年,对于项目经理来讲关注的很多是单项目管理的工具技术模板,谈论最多的是,进度延期,成本超支,范 ...

  8. JQuery OOP 及 OOP思想的简易理解

    在项目维护的时候,看到通篇的function实际上是非常费(痛)劲(苦),个人对于前端也不是特别熟悉,就想着JQuery能否也建立OOP的写法? 目的便于日后代码维护管理,就算不为了自己,日后交接后也 ...

  9. 队列工厂之RabbitMQ

    本次和大家分享的是RabbitMQ队列的用法,前一篇文章队列工厂之(MSMQ)中在描述的时候已经搭建了简单工厂,因此本章内容是在其之上扩充的子项不再过多讲解工厂的代码了:RabbitMQ应该是现在互联 ...

  10. [lua] mac上如何编译snapshot(检测Lua中的内存泄露)

    最近我们的unity手游频繁闪退,只要进入战斗场景,之后一段时间就会闪退,如果是在unity编辑器中则会报出not enough memory的错误!猜测应该是有内存泄漏: 由于我们使用了tolua, ...