一、什麼是異步編程?編程
異步編程就是把耗時的操做放進一個單獨的線程中進行處理(該線程須要將執行進度反映到界面上)。因爲耗時操做是在另一個線程中被執行的,因此它不會堵塞主線程。主線程開啓這些單獨的線程後,還能夠繼續執行其餘操做(例如窗體繪製等)。異步
異步編程能夠提升用戶體驗,避免在進行耗時操做時讓用戶看到程序「卡死」的現象。async
二、異步編程模型(APM)ide
APM是Asynchronous Programming Mode的縮寫,即異步編程模型的意思,它容許程序用更少的線程去執行更多的操做。在.NET Framework中,要分辨某個類是否實現了異步編程模型,主要就是看該類是否實現了返回類型爲IAsyncResult接口的BeginXXX方法和EndXXX方法。異步編程
因爲委託類型定義了BeginInvoke和EndInvoke方法,因此委託類型都實現了異步編程模型。spa
2.1 Beginxxx方法--開始執行異步操做線程
在須要獲取文件中的內容時,咱們一般會使用FileStream的同步方法Read進行讀取,該同步方法的定義爲:code
public override int Read(byte[] array,int offset,int count)對象
當使用上面的方法讀取大文件的內容時,會出現堵塞UI線程,致使在文件內容沒有讀取完成以前,用戶不能對窗體進行任何操做(包括關閉應用程序),這時窗體就會出現沒法響應的狀況。blog
爲了解決這個問題,微軟早在.NET 1.0的時候就提出了異步編程模型,併爲FileStream類提供了異步模式的方法實現,即BeginRead方法。該方法會異步地執行讀取操做,並返回實現了IAsyncResult接口的對象(該對象存儲這異步操做的信息)。
下面給出了BeginRead方法的定義,咱們能夠從中找出它與同步方法Read的區別:
public override IAsyncResult BeginRead(byte[] array,int offset,int numBytes,AsyncCallback userCallback,Object stateObject)
從以上的異步方法的定義能夠看出,該異步方法的前面3個參數與同步方法Read一致,後兩個參數userCallback和StateObject則是同步方法所不具有的。userCallback表示異步操做完成後須要回調的方法,該方法必須匹配AsyncCallback委託類型;stateObject則表明傳遞給回調方法的對象,在回調方法中,能夠經過查詢IAsyncResult接口的AsyncState屬性來讀取該對象。該異步方法之因此不會堵塞UI線程,是由於它在被調用後,會當即把控制權交還給調用線程(若是是UI線程調用了該方法,則就將控制權返回給UI線程),而後由另外一個線程去執行文件讀取操做。
2.2 Endxxx方法--結束異步操做
每次調用Beginxxx方法後,應用程序還需調用Endxxx方法來獲取操做返回的結果。Beginxxx方法所返回的,是實現了IAsyncResult接口的對象,該對象並不是相應的同步方法返回的結果。此時還須要調用Endxxx方法來結束異步操做,並向該方法傳遞Beginxxx所返回的對象。Endxxx方法返回的類型與同步方法相同,如FileStream的EndRead方法會返回一個Int32類型,表明從文件流中實際讀取的字節數。
Endxxx方法有許多中方式調用,但有一種是最經常使用的,即便用AsyncCallback委託來指定操做完成時要調用的方法,在回調方法中調用Endxxx方法來得到異步操做返回的結果。
1 static void Main() 2 { 3 SynchronizationContext sc=SynchronizationContext.Current; 4 AsyncMethodCaller methodCaller=new AsyncMethodCaller(DownLoadFileAsync); 5 method.BeginInvoke(txtUrl.Text.Trim(),GetResult,null); 6 } 7 private static void GetRsult(IAsyncResult result) 8 { 9 AsyncMethodCaller caller=(AsyncMethodCaller)((AsyncResult)result).AsyncDelegate; 10 string returnstring=call.EndInvoke(result); 11 }
三、異步編程模型(EAP)
雖然前面的異步編程能夠解決執行耗時操做時界面沒法響應的問題,但APM也一樣存在這一些明顯的問題,如不支持對異步操做的取消以及不能提供下載進度報告等。然而對於桌面應用程序而言,進度報告和取消操做的功能是必不可少的,因此微軟在.NET 2.0 發佈時又提出了一個新的異步編程模型--基於事件的異步模型,即EAP(Event-based Asynchronous Pattern)。
實現了EAP的類具備一個或多個以Async爲後綴的方法,以及對應的Completed事件,而且這些類支持異步方法的取消和進度報告。在.NET類庫中,只有部分類實現了EAP,共17個。在這17個類中,開發過程當中使用最多的莫過於BackgroundWorker類了。
常用的屬性爲:
CancellationPending:用來指示應用程序是否已請求取消後臺操做;
IsBusy:指示異步操做是否正在運行;
WorkReportProgress:只是BackgrounWorker可否報告進度;
WorkerSupportsCancellation:指示BackgroundWoker是否支持異步取消操做;
常用的方法爲:
CancelAsync:請求取消異步操做;
ReportProgress:用於引起ProgressChanged事件;
RunWorkAsync:調用後開始執行異步操做;
常用到的3個事件爲:
DoWork:調用RunWokerAsync時觸發的事件;
ProgressChanged:調用ReportProgress時觸發的事件,程序會在該事件中進行進度報告的更新;
RunWorkerCompleted:當異步操做已完成、被取消或引起異常時被觸發。
這種方法已經不多用到了,因此這裏就不詳細介紹了。
四、TAP又是什麼?
前面介紹了.NET提供的兩種異步編程模式,分別爲.NET 1.0中的APM和.NET 2.0中的EAP。雖然這兩種異步編程模式能夠實現多數狀況下的異步編程,可是它們在MSDN文檔上都被標註爲了避免推薦使用的實現方式,由於在.NET 4.0中,微軟又提供了更簡單的異步編程實現方式--TAP,基於任務的異步模式。
該模式主要使用System.Threading.Tasks命名空間中的Task<T>類來實現異步編程,因此在採用TAP以前,首先要引入System.Threading.Tasks命名空間。
基於任務的異步模式(TAP,Task-based Asynchronous Pattern)只使用一個方法就能表示異步操做的開始和完成,而APM卻須要Beginxxx和Endxxx兩個方法分別表示開始和結束,EAP則要求具備以Async爲後綴的方法和一個或多個事件。在基於任務的異步模式中,只須要一個以TaskAsync爲後綴的方法,經過向該方法傳入CancellationToken參數,咱們就能夠很好地完成異步編程了。並且,還能夠經過IProgress<T>接口來實現進度報告的功能。整體來講,使用TAP會減小咱們的工做量,是代碼更加簡潔。
1 Task task=new Task(()=>{.......}); 2 task.Start();
五、讓異步編程So easy——C# 5.0中的async和await
雖然.NET 1.0和.NET 2.0和.NET 4.0都對異步編程作了很好的支持,微軟也逐漸地使用異步編程變得簡單,但微軟以爲現有的工做還不夠,它但願使異步編程的開發過程更爲簡化,因此在.NET 4.5中,微軟又提出了async和await兩個關鍵字來支持異步編程。
這也是目前.NET Framework中最簡單的異步編程實現方式,由於使用這個兩個關鍵字進行異步編程,思考方式和實現同步編程時的徹底同樣。
async和await關鍵字不會讓調用方法運行在新線程中,而是將方法分割成多個片斷(片斷的界限出如今方法內部使用await關鍵字的位置處),並使其中一些片斷能夠異步運行。await關鍵字處的代碼片斷是在線程池線程上運行的,而整個方法的調用確實同步的。因此,使用此方式編程不用考慮跨線程訪問UI控件的問題,從而大大下降了異步編程的出錯率。