上篇已經講到Task 的默認的TaskScheduler 爲ThreadPoolTaskScheduler.app
這時咱們回到原來的task 的start方法,在代碼最後,調用了 ScheduleAndStart(true) 這個方法。接着看這個方法async
[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) == 0, "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) == 0) { //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, 0); } 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) == 0) { // 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。ide
// 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 類裏面有個靜態靜態字典,用於存放本身執行的類集合。固然說到管理和查詢,斷然我是不會放在這個類,令起新類也好。ui
這裏的代碼方法參數驗證都是採用契約驗證,其實我我的並非很贊同這東西,雖然C++也有這個。我倒更但願是本來的異常拋出,或者日誌記錄,或者其餘自定義方式。this
接着看核心方法 m_taskScheduler.InternalQueueTask(this); 前面咱們已經看到默認的m_taskScheduler爲ThreadPoolTaskScheduler。接着看代碼spa
[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) != 0) { // 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) != 0); 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是無法使用的(訪問修飾符),除非採用一些其餘手段,這裏很少介紹。因此只能本身從新去實現這個類的相關細節。