• C# Task 源代码阅读(1)


    平时我们开发中,经常使用Task,后续的.net版本种很多都和Task有关,比如asyn,await有了Task 我们很少就去关注Thread 了。Task 给我们带来了很多的便利之处。是我们更少的去关注执行的历程,更多的去关注逻辑。但是有些时候,有些应用。又不得不考虑task 的运行状况,比如这个任务成功与否,是否发生异常。经常听别人说到task 是在线程池执行的,那我们今天就来看看task 到底在做什么了,他执行的时候又做些哪些工作。

    大家可以从这里可以看到Task 的源代码,也可以从reference code 直接download 下来。

    我们先来看这段代码

    public class Task : IThreadPoolWorkItem, IAsyncResult, IDisposable
        {
            [ThreadStatic]
            internal static Task t_currentTask;  // The currently executing task.
            [ThreadStatic]
            private static StackGuard t_stackGuard;  // The stack guard object for this thread
    
            internal static int s_taskIdCounter; //static counter used to generate unique task IDs
            private readonly static TaskFactory s_factory = new TaskFactory();
    
            private volatile int m_taskId; // this task's unique ID. initialized only if it is ever requested
    
            internal object m_action;    // The body of the task.  Might be Action<object>, Action<TState> or Action.  Or possibly a Func.
            // If m_action is set to null it will indicate that we operate in the
            // "externally triggered completion" mode, which is exclusively meant 
            // for the signalling Task<TResult> (aka. promise). In this mode,
            // we don't call InnerInvoke() in response to a Wait(), but simply wait on
            // the completion event which will be set when the Future class calls Finish().
            // But the event would now be signalled if Cancel() is called
    }

    先看Task 类继承的接口,IThreadPoolItem 这个和线程池相关,IAsyncResult这个和异步执行的回掉相关,这里我不在过多说这个,

    接着我们看到有个字段t_currentTask ,而且是static 的,指向本身的task。大家不知道会不会有疑问,为什么这样设计呢,其实这样的设计在.net很多地方都有,比如HttpContext等等,特点基本都会有个Current。这种有点类似单例模式,但是开始已经初始化好,还有个更多的有点你可以随时替换,注入你自己的定义的东西。把他当作单例来用也是完全ok。注意了这里的访问修饰符是internal static。

    接着t_stackGuard,s_taskIdCounter 顾名思义不在过多介绍。

    下面就是s_factory 注意他是static 和访问修饰符,当然我如果用工厂模式,一般很少会把当前的工厂放在类内部来使用。哪天我要给我生产出的成品当然得这么做了。

    接着一个比较重要的字段m_action ,执行体。大家是否记得在汇编里是如何执行所谓函数的,push a push b call xxxx。a,b 分别是参数,xxxx 为跳转地址 执行代码,参数的传递一般是通过stack 来传递。在net 这里直接放成object ,而且注释写的很清楚无非是那些委托。但是对一个函数来说,他的执行体就是call 的地址。

    接着我们看下面的字段

            internal object m_stateObject; // A state object that can be optionally supplied, passed to action.
            internal TaskScheduler m_taskScheduler; // The task scheduler this task runs under. 
    
            internal readonly Task m_parent; // A task's parent, or null if parent-less.
    
    
            internal volatile int m_stateFlags;

    m_stateObject 一猜也大概直到作用。

    下面又是一个执行过程特别重要的字段m_taskScheduler,在执行过程比较重要。 大家平时windows 的平台的taskScheduler可能用的比较多,说到taskScheduler,功能也就是在合理时间安排合理的task 执行,实际上就是一个执行管理器。当然我们在sql server 的开发工具也有类似的工作,job 的执行,我们也是要选择执行计划的。当然这里的m_taskScheduler 也许是有本身的意思,都是任务调度器。当然task 默认的taskScheduler与我们刚刚提到的工具功能差距有点大。当然,大家有个印象,就是用来调度task 的。至于怎么调度,各自有各自的方案。

    m_stateFlags 状态标志字段。一个Task 的执行,我当然很想直到他当前的状态,开始,结束,所以这个也好理解。本身在Thread 种就有很多状态。

    继续看代码

       public void Start()
            {
                Start(TaskScheduler.Current);
            }
    public void Start(TaskScheduler scheduler)
            {
                // Read the volatile m_stateFlags field once and cache it for subsequent operations
                int flags = m_stateFlags;
    
                // Need to check this before (m_action == null) because completed tasks will
                // set m_action to null.  We would want to know if this is the reason that m_action == null.
                if (IsCompletedMethod(flags))
                {
                    throw new InvalidOperationException(Environment.GetResourceString("Task_Start_TaskCompleted"));
                }
    
                if (scheduler == null)
                {
                    throw new ArgumentNullException("scheduler");
                }
    
                var options = OptionsMethod(flags);
                if ((options & (TaskCreationOptions)InternalTaskOptions.PromiseTask) != 0)
                {
                    throw new InvalidOperationException(Environment.GetResourceString("Task_Start_Promise"));
                }
                if ((options & (TaskCreationOptions)InternalTaskOptions.ContinuationTask) != 0)
                {
                    throw new InvalidOperationException(Environment.GetResourceString("Task_Start_ContinuationTask"));
                }
    
                // Make sure that Task only gets started once.  Or else throw an exception.
                if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
                {
                    throw new InvalidOperationException(Environment.GetResourceString("Task_Start_AlreadyStarted"));
                }
    
                ScheduleAndStart(true);
            }

    我们平常都会用start方法,他会默认传入一个TaskScheduler,我们接着看下面的方法,最后调用的是ScheduleAndStart方法,不管前面的验证,我们重点看执行流程,要弄清这点,我们必须清楚TaskScheduler.Current

    到底是什么类,他的功能是什么,如果我们自己去写TaskScheduler,那又该去写什么,完成哪些功能。

    我们继续从reference code 找到TaskScheduler 类。我们先重点追踪Current ,先不管方法。

     public static TaskScheduler Current 
            {
                get
                {
                    TaskScheduler current = InternalCurrent;
                    return current ?? TaskScheduler.Default;
                }
            }
     internal static TaskScheduler InternalCurrent
            {
                get
                {
                    Task currentTask = Task.InternalCurrent;
                    return ( (currentTask != null) 
                        && ((currentTask.CreationOptions & TaskCreationOptions.HideScheduler) == 0)
                        ) ? currentTask.ExecutingTaskScheduler : null;
                }
            }
    默认我继续找到default 属性
    public static TaskScheduler Default 
            {
                get
                {
                    return s_defaultTaskScheduler;
                }
            }
       private static readonly TaskScheduler s_defaultTaskScheduler = new ThreadPoolTaskScheduler();
    我们一步一步追踪,终于找到了ThreadPoolTaskScheduler,这时终于可以task 把threadpool 联系起来了。

    再看执行
    ScheduleAndStart之前,我们看下
      if (Interlocked.CompareExchange(ref m_taskScheduler, scheduler, null) != null)
     这句的写法,null 判断再加上对象的赋值。这个我们可以在平时的代码中加以借用。



  • 相关阅读:
    恼人的CON(转)
    CINRAD/SB 多普勒天气雷达
    如何将两个栅格数据图层求和并求并集
    javascript设置asp.net程序开始运行时ie最大化显示
    ArcGIS Engine中数据的加载(转载)
    发现了一个arcgis engine的一个bug
    带余除法
    第二数学归纳法
    concat和concat_ws()区别及MySQL的几个实用字符串函数
    oracle查询语句大全
  • 原文地址:https://www.cnblogs.com/enuo/p/6668941.html
Copyright © 2020-2023  润新知