.Net在Framework4.0中增加了任務並行庫,對開發人員來說利用多核多線程CPU環境變得更加簡單,TPL正符合我們本系列的技術需求。因TPL涉及內容較多,且本系列文章為非同步程式開發,所以本文並未涉及TPL全部內容。後續會寫一個TPL系列的Blog,各位朋友可以關註一下。 ...
.Net在Framework4.0中增加了任務並行庫,對開發人員來說利用多核多線程CPU環境變得更加簡單,TPL正符合我們本系列的技術需求。因TPL涉及內容較多,且本系列文章為非同步程式開發,所以本文並未涉及TPL全部內容。後續會寫一個TPL系列的Blog,各位朋友可以關註一下。
TASK
TPL的基礎Task,Task是TPL並行編程的最小單元,即表示一個非同步操作。利用Task進行非同步編程非常簡單:
static void Main(string[] args) { BaseTaskDemo(); //BaseTaskDemo2();//兩者效果相同 Console.WriteLine(String.Format("Main 線程:{0},獲取非同步執行結果:{1}", Thread.CurrentThread.ManagedThreadId,task.Result)); Console.ReadLine(); } private static void BaseTaskDemo() { var task = new Task(() => { Thread.Sleep(2000); Console.WriteLine(String.Format("Task 線程:{0}", Thread.CurrentThread.ManagedThreadId)); }); task.Start(); } private static void BaseTaskDemo2() { var task = Task.Run(() => { Thread.Sleep(2000); Console.WriteLine(String.Format("Task 線程:{0}", Thread.CurrentThread.ManagedThreadId)); }); }
程式說明:
1、new Task(Action)創建一個非同步任務,參數Action是非同步任務要執行的委托對象
2、task.Start()啟動非同步任務的執行
3、Task.Run(Action)等效於1、2的組合
上面的示常式序實現了非同步操作,但主線程無法獲知非同步任務完成與否。為獲取Task的執行結果,上面的程式進行如下修改:
static void Main(string[] args) { var task = TaskForResult(); Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId)); task.Wait(); if (task.IsCompleted) Console.WriteLine(String.Format("獲取非同步執行結果:{0}", task.Result)); Console.ReadLine(); } private static Task<int> TaskForResult() { var task = Task.Run(()=> { Thread.Sleep(2000); Console.WriteLine(string.Format("Task 線程:{0},Task執行完成。", Thread.CurrentThread.ManagedThreadId)); return 10; }); return task; }
程式說明:
1、下麵代碼的原型為Task.Run(Func<int>),Run的參數不再是Action,因為在該任務中我們要返回一個int值,所以應該使用Run(Func<T>)這個重載。
var task = Task.Run(()=> { Thread.Sleep(2000); Console.WriteLine(string.Format("Task 線程:{0},Task執行完成。", Thread.CurrentThread.ManagedThreadId)); return 10; });
2、主線程中調用task.Wait()時,主線程將一直等待非同步任務完成或被取消。
3、task.IsCompleted屬性用於判斷非同步任務是否完成
4、task.Result獲取非同步任務的執行結果(返回值)
上面的示常式序已經實現了主線程獲取非同步程式的狀態及返回值,但如果非同步程式非常耗時,則存在主線程需要臨時取消耗時非同步程式執行的功能。為了滿足上述要求,程式可做如下調整:
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(5000); var task = TaskForResult2(tokenSource); Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId)); Console.WriteLine(String.Format("Main 線程:{0},獲取非同步執行結果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result)); Console.ReadLine(); } private static Task<int> TaskForResult2(CancellationTokenSource tokenSource) { var task = Task.Run(() => { Thread.Sleep(10000); if (!tokenSource.IsCancellationRequested) { Console.WriteLine(String.Format("Task 線程:{0},任務1執行完成。", Thread.CurrentThread.ManagedThreadId)); return 10; } else { return -1; } }, tokenSource.Token); return task; }
程式說明:
1、CancellationTokenSource提供任務取消消息,構造參數 5000 表示CancellationTokenSource在發出5s後超時並取消
2、在Task的委托內部 tokenSource.IsCancellationRequested 獲取取消標記
3、task.Result會隱式調用Wait()方法
如果非同步Task在執行過程中出現異常,則需要對發生的異常做出響應:
static void Main(string[] args) { CancellationTokenSource tokenSource = new CancellationTokenSource(5000); var task = TaskForResult2(tokenSource); Console.WriteLine(String.Format("Main 線程:{0}", Thread.CurrentThread.ManagedThreadId)); try { //task.Wait(); Console.WriteLine(String.Format("Main 線程:{0},獲取非同步執行結果:{1}", Thread.CurrentThread.ManagedThreadId, task.Result)); } catch (AggregateException ex) { } Console.ReadLine(); } private static Task<int> TaskForResult2(CancellationTokenSource tokenSource) { var task = Task.Run(() => { Thread.Sleep(1000); if (!tokenSource.IsCancellationRequested) { throw new Exception("拋出異常"); } else { return -1; } }, tokenSource.Token); return task; }
程式說明:
1、在Task中引發的異常需要在 task.Wait()或task.Result時捕獲
寫在後面:Task的功能遠不止上述這些,如Task多任務串列、TaskFactory、Paralle等知識非常有趣和重要。如果你感興趣的話,可以關註本人後續TPL的文章。