多線程——Task,多線程task
背景:
以前想用Semaphore來處理並發訪問資源的問題,後來把方案拿給前輩們看的時候,他們說這樣也行,但是最好用Task處理,比較簡單。所以,順便學習了下Task.
使用task類建立並執行簡單任務
等待任務的完成並擷取返回值
使用ContinueWith方法在任務完成時啟動一個新任務
建立父子任務和任務工廠的使用
<span style="font-size:12px;">namespace 建立父子任務和任務工廠的使用{ class Program { //通過task類建立的任務是頂級任務,可以通過使用TaskCreationOptions .AttachedToParent 標識把這些任務與建立他的任務相關聯 //所有子任務完成以後父任務才會結束操作 // static void Main(string[] args) { #region 建立父子任務Demo //Task<string[]> parent = new Task<string[]>(state => { // Console.WriteLine(state); // string[] result = new string[2]; // //建立並啟動子任務 // new Task(() => { result[0] = "任務1。。。"; },TaskCreationOptions .AttachedToParent ).Start (); // new Task(() => { result [1]="任務2。。。";},TaskCreationOptions .AttachedToParent ).Start (); // return result ; // },"這裡是父任務,並在處理過程中建立多個子任務,所有子任務完成以後才會執行。"); // //任務處理完成後執行的操作 // parent .ContinueWith (t=>{ // Array.ForEach(t.Result, r => Console.WriteLine(r)); // }); // //啟動父任務 // parent .Start (); // Console .ReadKey (); #endregion #region 任務工廠的使用 Task parent=new Task(()=>{ CancellationTokenSource cts = new CancellationTokenSource();//為什麼不包含一個參數的建構函式?????? //建立任務工廠 TaskFactory tf = new TaskFactory(cts.Token, TaskCreationOptions.AttachedToParent, TaskContinuationOptions.ExecuteSynchronously, TaskScheduler.Default); //添加一組具有相同狀態的子任務 Task[] task=new Task[]{ tf.StartNew (()=>{Console.WriteLine("我是任務工廠裡的第一個任務");}), tf.StartNew (()=>{Console .WriteLine("我是任務工廠裡的第二個任務");}), tf.StartNew (()=>{Console .WriteLine("我是任務工廠裡的第三個任務");}) }; }); parent .Start(); Console.ReadKey(); #endregion } }}</span>
任務內部的實現和調度
任務內部有一組構成任務狀態的屬性,表示任務的唯一ID,表示任務的執行狀態(TaskStatus),任務建立時提供的回呼函數的引用和傳遞給回呼函數的資料對象AsyncState,對任務建立時的任務調度對象(TaskScheduler)的引用,對父任務的引用以及對執行內容的引用和ManualResetEventSlim對象的引用。
Task和Task<TResult>類都實現了標準的釋放資源的介面,允許在任務完成處理的時候使用Dispose方法釋放資源(關閉ManualResetEventSlim對象執行個體)。可以使用Task類的CurrentID屬性獲得正在執行的任務的ID,如果沒有任務在執行CurrentID返回的值為null,currentID是一個int,可空類型的屬性。任務執行的生命週期通過TaskStatus類似的一個值來表示,TaskStatus所包含的值為taskstatus
namespace 任務內部的實現和調度{ class Program { static void TestDemo() { //獲得同步上下文任務調度器 TaskScheduler m_syncContextTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();//表示一個處理將任務排隊到線程中的低級工作對象 //建立任務,並採用預設任務調度(線程池任務調度器)執行任務 Task<int> task = new Task<int>(() => { //執行複雜的計算任務 Thread.Sleep(2000);//當前線程暫停2000 int sum = 0; for (int i = 0; i < 100; i++) { sum += i; } return sum; }); var cts = new CancellationTokenSource(); //任務完成時啟動一個後續任務,並採用同步上下文任務調度器調度任務更新UI組件 task.ContinueWith(t => { Console.WriteLine("採用SynchronizationContextTaskScheduler任務調度器更新UI。\r\n計算結果是:" + task.Result.ToString()); }, cts.Token, TaskContinuationOptions.AttachedToParent, m_syncContextTaskScheduler); task.Start(); } static void Main(string[] args) { TestDemo(); } } #region TaskStatus所包含的值: // public enum TaskStatus //{ // Created = 0, // WaitingForActivation = 1, // WaitingToRun = 2, // Running = 3, // WaitingForChildrenToComplete = 4, // RanToCompletion = 5, // Canceled = 6, // Faulted = 7 //} #endregion}
我們可以通過Task類的Exception屬性獲得任務在執行過程中的所有異常,Exception是一個AggregateException類型的屬性。Task類提供了IsCanceled、IsCompleted、IsFaulted屬性來獲得任務的完成狀態。通過ContinueWith、ContinueWhenAll、ContinueWhenAny和FromAsync建立的後續任務都處於WaitingForActivation狀態,這個狀態的任務會在父任務完成後自動執行。
在任務內部由TaskScheduler類調度任務的執行,該類是一個抽象類別,FCL中從他派生了兩個衍生類別:ThreadPoolTaskScheduler線程池任務調度器和SynchronizationContextTaskScheduler同步上下文任務調度器。所有任務預設都是採用ThreadPoolTaskScheduler調度任務,他是採用線程池來執行任務,可以通過TaskScheduler類的靜態屬性Default獲得對預設任務調度器的引用。SynchronizationContextTaskScheduler任務調度器能夠用在Windowform、WPF等應用程式,他的任務調度是採用的GUI線程,所以他能同步更新UI組件,可以通過TaskScheduler類的靜態方法FromCurrentSynchronizationContext獲得對一個同步上下文任務調度起的引用。