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 判斷再加上對象的賦值。這個我們可以在平時的代碼中加以借用。



 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM