前面介紹了Task的由來,以及簡單的使用,包括開啟任務,處理任務的超時、異常、取消、以及如果獲取任務的返回值,在回去返回值之后,立即喚起新的線程處理返回值、且如果前面的任務發生異常,喚起任務如果有效的處理異常等關於Task的知識。所以本文將介紹Task更多的用法和特性.
一、如果通過一個任務創建多個子任務.
1、Task支持一個任務,創建多個子任務,並且保持關聯.
static void Main(string[] args) { var parentTask = new Task<int[]>(() => { //開啟多個子任務 var results = new int[2]; //創建子任務,並將子任務的值賦給results變量,並通過TaskCreationOptions.AttachedToParent,將其關聯到父任務,如果不指定,該任務將獨立於父任務單獨執行 //這里有個奇怪的問題,只能使用new Task的方式去創建關聯到父任務的子任務,因為Task.Run沒有提供這個方法,可以通過擴展方法解決這個問題 new Task(() => results[0] = ChildThreadOne(), TaskCreationOptions.AttachedToParent).Start(); new Task(() => results[1] = ChildThreadTwo(), TaskCreationOptions.AttachedToParent).Start(); return results; }); parentTask.Start(); parentTask.ContinueWith(x => { Console.WriteLine("當父任務執行完畢時,CLR會喚起一個新線程,將父任務的返回值(子任務的返回值)輸出,所以這里不會有任何的線程發生阻塞"); foreach (var re in parentTask.Result) { Console.WriteLine("子任務的返回值分別為:{0}", re); } }); Console.WriteLine("主線程不會阻塞,它會繼續執行"); Console.ReadKey();//必須加這行代碼,因為Task時線程池線程,屬於后台線程 } /// <summary> /// 子任務一 /// </summary> static int ChildThreadOne() { Thread.Sleep(2000);//模擬長時間計算操作 Console.WriteLine("子任務一完成了計算任務,並返回值:{0}", 6); return 6; } /// <summary> /// 子任務一 /// </summary> static int ChildThreadTwo() { Thread.Sleep(2000);//模擬長時間計算操作 Console.WriteLine("子任務二完成了計算任務,並返回值:{0}", 6); return 6; }
二、關於Task的資源釋放問題.
如果你看過Task的源碼,你會發現下面這個有趣的問題:
ok,你會想它想釋放什么呢?
沒錯,當Task任務,指定了TaskContinuationOptions枚舉狀態,且指定的值如下:
那么,直接return,什么資源釋放操作都不做.
如果任務沒有完成,就調用Dispose方法,那么直接拋異常,如果完成了,它就釋放了ManualResetEventSlim信號量(后面的文章會介紹).所以如果你在task中使用了其它的一些非托管資源,那么最好在代碼里自己手動釋放,在使用完之后。或者自己實現了Task的派生類,把需要用的非托管資源加進去,然后在使用完派生類之后,調用Dispose方法.
三、關於Task的幾個常用屬性
1、Id屬性,每個Task對象都有一個Id屬性,全局唯一,且每次創建新的任務,這個值都會遞增1.
2、TaskStatus狀態
// // 摘要: // 表示 System.Threading.Tasks.Task 的生命周期中的當前階段。 public enum TaskStatus { // // 摘要: // 該任務已初始化,但尚未被計划。 Created = 0, // // 摘要: // 該任務正在等待 .NET Framework 基礎結構在內部將其激活並進行計划。 WaitingForActivation = 1, // // 摘要: // 該任務已被計划執行,但尚未開始執行。 WaitingToRun = 2, // // 摘要: // 該任務正在運行,但尚未完成。 Running = 3, // // 摘要: // 該任務已完成執行,正在隱式等待附加的子任務完成。 WaitingForChildrenToComplete = 4, // // 摘要: // 已成功完成執行的任務。 RanToCompletion = 5, // // 摘要: // 該任務已通過對其自身的 CancellationToken 引發 OperationCanceledException 對取消進行了確認,此時該標記處於已發送信號狀態;或者在該任務開始執行之前,已向該任務的 // CancellationToken 發出了信號。 有關詳細信息,請參閱任務取消。 Canceled = 6, // // 摘要: // 由於未處理異常的原因而完成的任務。 Faulted = 7 }
構造完Task對象是,狀態為Created,當任務啟動時,狀態變為WaitingToRun,當Task實際在線程上運行時,狀態變為Running.如果當前任務為父任務,且它已經執行完畢,等待其它子任務執行完畢的時候,其狀態變為WaitingForChildrenToComplete.如果任務完成可能會出現以下幾種狀態:RanToCompletion(已成功完成執行的任務)、Canceled(取消狀態)、
Faulted(任務出錯).
這里需要注意一個特殊的狀態WaitingForActivation
當使用Task對象的ContinueWith的Task對象處理改狀態,意味者該Task任務的調度由任務基礎結構控制.也就是該任務的調度只有當前面的任務執行完之后,由CLR發起執行調用.