不得不說的異步編程

一、什麼是異步編程?編程

    異步編程就是把耗時的操做放進一個單獨的線程中進行處理(該線程須要將執行進度反映到界面上)。因爲耗時操做是在另一個線程中被執行的,因此它不會堵塞主線程。主線程開啓這些單獨的線程後,還能夠繼續執行其餘操做(例如窗體繪製等)。異步

    異步編程能夠提升用戶體驗,避免在進行耗時操做時讓用戶看到程序「卡死」的現象。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控件的問題,從而大大下降了異步編程的出錯率。

相關文章
相關標籤/搜索