程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> .NET網頁編程 >> C# >> C#入門知識 >> 不得不說的異步編程,不得不說異步編程

不得不說的異步編程,不得不說異步編程

編輯:C#入門知識

不得不說的異步編程,不得不說異步編程


1、什麼是異步編程?

    異步編程就是把耗時的操作放進一個單獨的線程中進行處理(該線程需要將執行進度反映到界面上)。由於耗時操作是在另外一個線程中被執行的,所以它不會堵塞主線程。主線程開啟這些單獨的線程後,還可以繼續執行其他操作(例如窗體繪制等)。

    異步編程可以提高用戶體驗,避免在進行耗時操作時讓用戶看到程序“卡死”的現象。

 

2、異步編程模型(APM)

    APM是Asynchronous Programming Mode的縮寫,即異步編程模型的意思,它允許程序用更少的線程去執行更多的操作。在.NET Framework中,要分辨某個類是否實現了異步編程模型,主要就是看該類是否實現了返回類型為IAsyncResult接口的BeginXXX方法和EndXXX方法。

    由於委托類型定義了BeginInvoke和EndInvoke方法,所以委托類型都實現了異步編程模型。

    2.1 Beginxxx方法--開始執行異步操作

          在需要獲取文件中的內容時,我們通常會使用FileStream的同步方法Read進行讀取,該同步方法的定義為:

          public override int Read(byte[] array,int offset,int count)

          當使用上面的方法讀取大文件的內容時,會出現堵塞UI線程,導致在文件內容沒有讀取完成之前,用戶不能對窗體進行任何操作(包括關閉應用程序),這時窗體就會出現無法響應的情況。

          為了解決這個問題,微軟早在.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 }

 

3、異步編程模型(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:當異步操作已完成、被取消或引發異常時被觸發。

    這種方法已經很少用到了,所以這裡就不詳細介紹了。

 

4、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();

 

5、讓異步編程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控件的問題,從而大大降低了異步編程的出錯率。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved