5天玩轉C#並行和多線程編程系列文章目錄html
5天玩轉C#並行和多線程編程 —— 第一天 認識Parallel編程
5天玩轉C#並行和多線程編程 —— 次日 並行集合和PLinq多線程
5天玩轉C#並行和多線程編程 —— 第三天 認識和使用Task架構
5天玩轉C#並行和多線程編程 —— 第四天 Task進階異步
5天玩轉C#並行和多線程編程 —— 第五天 多線程編程大總結函數
對於多線程,咱們常用的是Thread。在咱們瞭解Task以前,若是咱們要使用多核的功能可能就會本身來開線程,然而這種線程模型在.net 4.0以後被一種稱爲基於「任務的編程模型」所衝擊,由於task會比thread具備更小的性能開銷,不過你們確定會有疑惑,任務和線程到底有什麼區別呢?post
任務和線程的區別:性能
一、任務是架構在線程之上的,也就是說任務最終仍是要拋給線程去執行。spa
二、任務跟線程不是一對一的關係,好比開10個任務並非說會開10個線程,這一點任務有點相似線程池,可是任務相比線程池有很小的開銷和精確的控制。.net
一、認識Task
首先來看一下Task的繼承結構。Task標識一個異步操做。
能夠看到Task和Thread同樣,位於System.Threading命名空間下,這也就是說他們直接有密不可分的聯繫。下面咱們來仔細看一下吧!
二、建立Task
建立Task的方法有兩種,一種是直接建立——new一個出來,一種是經過工廠建立。下面來看一下這兩種建立方法:
//第一種建立方式,直接實例化 var task1 = new Task(() => { //TODO you code });
這是最簡單的建立方法,能夠看到其構造函數是一個Action,其構造函數有以下幾種,比較經常使用的是前兩種。
//第二種建立方式,工廠建立 var task2 = Task.Factory.StartNew(() => { //TODO you code });
這種方式經過靜態工廠,建立以個Task並運行。下面咱們來建一個控制檯項目,演示一下,代碼以下:
要添加System.Threading.Tasks命名控件引用。
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace TaskDemo { class Program { static void Main(string[] args) { var task1 = new Task(() => { Console.WriteLine("Hello,task"); }); task1.Start(); var task2 = Task.Factory.StartNew(() => { Console.WriteLine("Hello,task started by task factory"); }); Console.Read(); } } }
這裏我分別用兩種方式建立兩個task,並讓他們運行。能夠看到經過構造函數建立的task,必須手動Start,而經過工廠建立的Task直接就啓動了。
下面咱們來看一下Task的聲明週期,編寫以下代碼:
var task1 = new Task(() => { Console.WriteLine("Begin"); System.Threading.Thread.Sleep(2000); Console.WriteLine("Finish"); }); Console.WriteLine("Before start:" + task1.Status); task1.Start(); Console.WriteLine("After start:" + task1.Status); task1.Wait(); Console.WriteLine("After Finish:" + task1.Status); Console.Read();
task1.Status就是輸出task的當前狀態,其輸出結果以下:
能夠看到調用Start前的狀態是Created,而後等待分配線程去執行,到最後執行完成。
從咱們能夠得出Task的簡略生命週期:
Created:表示默認初始化任務,可是「工廠建立的」實例直接跳過。
WaitingToRun: 這種狀態表示等待任務調度器分配線程給任務執行。
RanToCompletion:任務執行完畢。
Task最吸引人的地方就是他的任務控制了,你能夠很好的控制task的執行順序,讓多個task有序的工做。下面來詳細說一下:
一、Task.Wait
在上個例子中,咱們已經使用過了,task1.Wait();就是等待任務執行完成,咱們能夠看到最後task1的狀態變爲Completed。
二、Task.WaitAll
看字面意思就知道,就是等待全部的任務都執行完成,下面咱們來寫一段代碼演示一下:
static void Main(string[] args) { var task1 = new Task(() => { Console.WriteLine("Task 1 Begin"); System.Threading.Thread.Sleep(2000); Console.WriteLine("Task 1 Finish"); }); var task2 = new Task(() => { Console.WriteLine("Task 2 Begin"); System.Threading.Thread.Sleep(3000); Console.WriteLine("Task 2 Finish"); }); task1.Start(); task2.Start(); Task.WaitAll(task1, task2); Console.WriteLine("All task finished!"); Console.Read(); }
其輸出結果以下:
能夠看到,任務一和任務二都完成之後,才輸出All task finished!
三、Task.WaitAny
這個用發同Task.WaitAll,就是等待任何一個任務完成就繼續向下執行,將上面的代碼WaitAll替換爲WaitAny,輸出結果以下:
四、Task.ContinueWith
就是在第一個Task完成後自動啓動下一個Task,實現Task的延續,下面咱們來看下他的用法,編寫以下代碼:
static void Main(string[] args) { var task1 = new Task(() => { Console.WriteLine("Task 1 Begin"); System.Threading.Thread.Sleep(2000); Console.WriteLine("Task 1 Finish"); }); var task2 = new Task(() => { Console.WriteLine("Task 2 Begin"); System.Threading.Thread.Sleep(3000); Console.WriteLine("Task 2 Finish"); }); task1.Start(); task2.Start(); var result = task1.ContinueWith<string>(task => { Console.WriteLine("task1 finished!"); return "This is task result!"; }); Console.WriteLine(result.Result.ToString()); Console.Read(); }
執行結果以下:
能夠看到,task1完成以後,開始執行後面的內容,而且這裏咱們取得task的返回值。
在每次調用ContinueWith方法時,每次會把上次Task的引用傳入進來,以便檢測上次Task的狀態,好比咱們可使用上次Task的Result屬性來獲取返回值。咱們還能夠這麼寫:
var SendFeedBackTask = Task.Factory.StartNew(() => { Console.WriteLine("Get some Data!"); }) .ContinueWith<bool>(s => { return true; }) .ContinueWith<string>(r => { if (r.Result) { return "Finished"; } else { return "Error"; } }); Console.WriteLine(SendFeedBackTask.Result);
首先輸出Get some data,而後執行第二個得到返回值true,最後根據判斷返回Finished或error。輸出結果:
Get some Data!
Finished
其實上面的寫法簡化一下,能夠這樣寫:
Task.Factory.StartNew<string>(() => {return "One";}).ContinueWith(ss => { Console.WriteLine(ss.Result);});
輸出One,這個能夠看明白了吧~
更多ContinueWith用法參見:http://technet.microsoft.com/zh-CN/library/dd321405
五、Task的取消
前面說了那麼多Task的用法,下面來講下Task的取消,好比咱們啓動了一個task,出現異常或者用戶點擊取消等等,咱們能夠取消這個任務。
如何取消一個Task呢,咱們經過cancellation的tokens來取消一個Task。在不少Task的Body裏面包含循環,咱們能夠在輪詢的時候判斷IsCancellationRequested屬性是否爲True,若是是True的話就return或者拋出異常,拋出異常後面再說,由於尚未說異常處理的東西。
下面在代碼中看下如何實現任務的取消,代碼以下:
var tokenSource = new CancellationTokenSource(); var token = tokenSource.Token; var task = Task.Factory.StartNew(() => { for (var i = 0; i < 1000; i++) { System.Threading.Thread.Sleep(1000); if (token.IsCancellationRequested) { Console.WriteLine("Abort mission success!"); return; } } }, token); token.Register(() => { Console.WriteLine("Canceled"); }); Console.WriteLine("Press enter to cancel task..."); Console.ReadKey(); tokenSource.Cancel();
Console.ReadKey();//這句忘了加,程序退出了,看不到「Abort mission success!「這個提示
這裏開啓了一個Task,並給token註冊了一個方法,輸出一條信息,而後執行ReadKey開始等待用戶輸入,用戶點擊回車後,執行tokenSource.Cancel方法,取消任務。其輸出結果以下:
好了,今天先說道這裏,明天繼續講task,接下來該說說task的異常處理和其餘的一些用法,若是喜歡能夠關注我,一有更新會立刻通知你。
做者:雲霏霏
博客地址:http://www.cnblogs.com/yunfeifei/
聲明:本博客原創文字只表明本人工做中在某一時間內總結的觀點或結論,與本人所在單位沒有直接利益關係。非商業,未受權,貼子請以現狀保留,轉載時必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接。