天天看點

基于事件的異步調用模式

基于事件的異步模式(EAP模式)

基于事件的C#異步程式設計模式是比IAsyncResult模式更進階的一種異步程式設計模式,也被用在更多的場合。該異步模式具有以下優點:

·                  “在背景”執行耗時任務(例如下載下傳和資料庫操作),但不會中斷您的應用程式。

·                  同時執行多個操作,每個操作完成時都會接到通知(在通知中可以區分是完成了哪個操作)。

·                  等待資源變得可用,但不會停止(“挂起”)您的應用程式。

·                  使用熟悉的事件和委托模型與挂起的異步操作通信。

實作 了EAP模式的最典型元件是WebClient。

WebClient定義了以下兩個同步方法用于從WEB上下載下傳檔案:

public void DownloadFile(string address, string fileName);

public void DownloadFile(Uri address, string fileName);      

為了實作異步調用,WebClient又定義了另兩個對應的異步方法:

public void DownloadFileAsync(Uri address, string fileName);

public void DownloadFileAsync(Uri address, string fileName, object userToken);      

EAP規定方法名以 Async 結尾的方法是異步調用方法。

上面的方法,最後一個userToken參數前面已經說明了其作用。

其實很簡單,當應用程式多次調用DownloadFileAsync方法的這個重載形式啟動多個異步下載下傳任務時,這個參數用于區分這些任務。簡單地說,userToken

是異步下載下傳任務的辨別。為每個正在執行的下載下傳任務給出唯一的辨別,是程式員的責任。

為了讓使用者中途取消任務,WebClient定義了以下方法:

public void CancelAsync();

由于任務可以被取消,是以問題産生了:異步調用方法啟動以後,調用者如何知道任務是正常結束還是被中途取消的?

答案很簡單:異步調用任務結束時,實作 了EAP的元件會激發一個相應的事件。以WebClient為例,當DownloadFileAsync方法啟動的異步下載下傳任務結束時,它會激發以下事件:

public event AsyncCompletedEventHandler DownloadFileCompleted;

這一事件有一個 AsyncCompletedEventArgs 類型的參數,其中包含了重要的資訊:

​​​​

public class AsyncCompletedEventArgs : EventArgs
    {

        public bool Cancelled { get; } //該值訓示異步操作是否已被取消。

        public Exception Error { get; }//該值訓示異步操作期間發生的錯誤。
    
        public object UserState { get; }//擷取異步任務的唯一辨別符,如果使用者在啟動本任務時設定了任務辨別,則此屬性值就是這個辨別

    }      

​​​​

由此可見,隻需在 DownloadFileCompleted事件的響應方法中檢查一下事件參數的Cancelled屬性,如果其值等于 true,就知道任務被中途取消了。

如果error屬性不為null,則任務執行過程中一定引發了異常。上述兩個條件都不滿足時,則任務是順利完成的。

當異步任務可能要執行很長時間時,往往需要通知使用者目前工作完成的進度,為此,WebClient定義了以下事件:

public event DownloadProgressChangedEventHandler DownloadProgressChanged;

這個事件的參數中(DownloadProgressChangedEventArgs類型的對象)中包含了重要的資訊。

1. TotalBytesToReceive:要傳送的總位元組數。

2. BytesReceived:已接收的位元組數。

3.ProgressPercentage:工作完成的百分比。

掌握了以上知識,使用WebClient元件下載下傳檔案就變得非常簡單,以下是一個代碼架構:

​​​​

//從Web異步下載下傳網址為FileAddress的檔案

public void DownLoadFileFromWeb(string FileAddress)

{

  WebClient client = new WebClient();

  //挂接下載下傳完成事件響應代碼

  client.DownloadFileCompleted += client_DownloadFileCompleted;

  //挂接下載下傳進度事件響應代碼

  client.DownloadProgressChanged += client_DownloadProgressChanged;

  //啟動異步下載下傳檔案任務

  Uri uri = new Uri(FileAddress);

  client.DownloadStringAsync(uri);

}      

​​​​

下載下傳完成事件響應代碼架構如下:

​​​​

void client_DownloadFileCompleted(object sender,AsyncCompletedEventArgs e)

{

  if(e.Cancelled)    //使用者取消了操作

  {

       //處理代碼。。。。

  }

  if(e.Error != null)   //有錯誤發生

  {

      //處理代碼 

  }

  if(e.UserState != null)   //取出任務辨別

  {

    //處理代碼

  }

    //其它處理代碼。。。



}      

​​​​

下載下傳進度事件響應代碼架構如下:

​​​​

void client_DownloadProgressChanged(object sender,DownloadProgressChangedEventArgs e)

{

   string info = "任務辨別:{0},總資料:{1}位元組,已下載下傳:{2}位元組,完成了{3}%。";

   info = string.Format(info , e.UserState , e.TotalBytesToReceive , e.BytesReceived , e.ProgressPercentage);

   //.....

}      

​​​​

依據前面的内容,我們可以對EAP形成以下的認識:

1.實作了EAP的元件定義 了以 Async 結尾的異步調用方法。

2.當異步調用任務結束時,會激發一個相應的事件,事件的參數包含重要的資訊。

3.實作 了EAP的元件可能會提供一個用于取消異步任務的方法。

4.實作 了EAP的元件提供一個向使用者報告進度的事件。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Net;
using System.ComponentModel;
namespace ConsoleApplication1
{
    public class EventAsyncModel
    {
        //從Web異步下載下傳網址為FileAddress的檔案

        public void DownLoadFileFromWeb(string FileAddress)
        {
            WebClient client = new WebClient();
            //挂接下載下傳完成事件響應代碼
            client.DownloadFileCompleted += client_DownloadFileCompleted;
            //挂接下載下傳進度事件響應代碼
            client.DownloadProgressChanged += client_DownloadProgressChanged;
            //啟動異步下載下傳檔案任務
            Uri uri = new Uri(FileAddress);
            client.DownloadFileAsync(uri,@"C:\QQ.EXE");

        }

        void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
        {
            if (e.Cancelled)    //使用者取消了操作
            {
                //處理代碼。。。。
                Console.WriteLine("cancel");
            }
            if (e.Error != null)   //有錯誤發生
            {
                //處理代碼 
                Console.WriteLine("發生錯誤" + e.Error.ToString());
            }
            if (e.UserState != null)   //取出任務辨別
            {
                //處理代碼
                Console.WriteLine("任務狀态" + e.UserState.ToString());
            }
            //其它處理代碼。。。
            Console.WriteLine("下載下傳結束呢");
        }

        void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
        {
            string info = "任務辨別:{0},總資料:{1}位元組,已下載下傳:{2}位元組,完成了{3}%。";
            info = string.Format(info, e.UserState, e.TotalBytesToReceive, e.BytesReceived, e.ProgressPercentage);
            Console.WriteLine(info);
        }
    }
}      

繼續閱讀