前言:前面有篇從應用層面上面介紹了下多線程的幾種用法,有博友就說到了async, await等新文法。确實,沒有異步的多線程是單調的、乏味的,async和await是出現在C#5.0之後,它的出現給了異步并行變成帶來了很大的友善。異步程式設計涉及到的東西還是比較多,本篇還是先介紹下async和await的原理及簡單實作。
C#基礎系列目錄:
C#基礎系列——Linq to Xml讀寫xml
C#基礎系列——擴充方法的使用
C#基礎系列——序列化效率比拼
C#基礎系列——反射筆記
C#基礎系列——Attribute特性使用
C#基礎系列——小話泛型
C#基礎系列——多線程的常見用法詳解
C#基礎系列——委托和設計模式(一)
C#基礎系列——委托和設計模式(二)
C#基礎系列——再也不用擔心面試官問我“事件”了
之前的那篇 C#基礎系列——多線程的常見用法詳解 就講到了多線程new Thread()的方式對于有傳回值類型的委托是沒有解決方案的,如果需要傳回值,必須要依靠異步的方式。了解異步之前,我們先來看看Thread對象的更新版本Task對象:
1、Task對象的前世今生:Task對象是.Net Framework 4.0之後出現的異步程式設計的一個重要對象。在一定程度上來說,Task對象可以了解Thread對象的一個更新産品。既然是更新産品,那它肯定有他的優勢,比如我們上面Thread對象不能解決的問題:對于有傳回值類型的委托。Task對象就能簡單的解決。
先來看結果:

從結果分析可知在執行var strRes = Task.Run<string>(() => { return GetReturnResult(); })這一句後,主線程并沒有阻塞去執行GetReturnResult()方法,而是開啟了另一個線程去執行GetReturnResult()方法。直到執行strRes.Result這一句的時候主線程才會等待GetReturnResult()方法執行完畢。為什麼說是開啟了另一個線程,我們通過線程ID可以看得更清楚:
結果:
由此可以得知,Task.Run<string>(()=>{}).Reslut是阻塞主線程的,因為主線程要得到傳回值,必須要等方法執行完成。
Task對象的用法如下:
由上可知,Task對象的構造函數傳入的是一個委托,既然能傳入Action類型的委托,可想而知Action的16中類型的參數又可以派上用場了。于是乎Task對象參數的傳遞就不用多說了吧。詳見 C#基礎系列——委托和設計模式(一)裡面Action委托的用法。
2、初識 async & await。
我們來看看程式的執行過程:
由上面的結果可以得到如下結論:
(1)在async辨別的方法體裡面,如果沒有await關鍵字的出現,那麼這種方法和調用普通的方法沒什麼差別。
(2)在async辨別的方法體裡面,在await關鍵字出現之前,還是主線程順序調用的,直到await關鍵字的出現才會出現線程阻塞。
(3)await關鍵字可以了解為等待方法執行完畢,除了可以标記有async關鍵字的方法外,還能标記Task對象,表示等待該線程執行完畢。是以await關鍵字并不是針對于async的方法,而是針對async方法所傳回給我們的Task。
(4)是否async關鍵字隻能辨別傳回Task對象的方法呢。我們來試試:
異步方法的傳回類型必須為void、Task或者Task<T>類型。也就是說async要麼是void,要麼和Task關聯。
3、除了await關鍵字,Task對象還有另外一種方式等待執行結果。
這樣可以得到相同的結果。
name.GetAwaiter()這個方法得到的是一個TaskAwaiter對象,這個對象表示等待完成的異步任務的對象,并提供結果的參數。是以除了能完成await關鍵字的等待之外,它還能做一些其他的操作。我們将TaskAwaiter轉到定義
IsCompleted:擷取一個值,該值訓示異步任務是否已完成。
GetResult():得到執行的結果。這個方法和await關鍵字效果相同。
OnCompleted():傳入一個委托,在任務執行完成之後執行。
UnsafeOnCompleted():計劃與此 awaiter 相關異步任務的延續操作。
由此可以看出,await關鍵字實際上就是調用了TaskAwaiter對象的GetResult()方法。