溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

基于事件的異步編程模式EMP如何理解

發(fā)布時間:2022-01-11 09:36:11 來源:億速云 閱讀:107 作者:柒染 欄目:編程語言

基于事件的異步編程模式EMP如何理解,針對這個問題,這篇文章詳細介紹了相對應的分析和解答,希望可以幫助更多想解決這個問題的小伙伴找到更簡單易行的方法。

.NET1.0中基于IAsyncResult設計模式的異步編程模型(APM),它使用System.Threading命名空間的類來構(gòu)造多線程應用程序。然而要想有效地使用這些工具類,需要有豐富的使用多線程軟件工程的經(jīng)驗。對于相對簡單的多線程應用程序,BackgroundWorker組件提供了一個簡單的解決方案。對于更復雜的異步應用程序,可以考慮實現(xiàn)一個符合基于事件的異步模式的類。

使用支持此模式的類,您將能夠:

1) 異步執(zhí)行耗時的任務。

2) 獲得進度報告和增量結(jié)果。

3) 支持耗時任務的取消。

4) 獲得任務的結(jié)果值或異常信息。

5) 更復雜:支持同時執(zhí)行多個異步操作、進度報告、增量結(jié)果、取消操作、返回結(jié)果值或異常信息。

源碼下載:異步編程:基于事件的異步模型(EAP).rar

為了實現(xiàn)基于事件的異步模式,我們必須先理解兩個重要的幫助器類:

AsyncOperationManager和AsyncOperation

AsyncOperationManager類和AsyncOperation類是System.ComponentModel命名空間為我們提供了兩個重要幫助器類。在基于事件的異步模式封裝標準化的異步功能中,它確保你的異步操作支持在各種應用程序模型(包括 ASP.NET、控制臺應用程序和 Windows 窗體應用程序)的適當“線程或上下文”調(diào)用客戶端事件處理程序。

AsyncOperationManager類和AsyncOperation類的API如下:

// 為支持異步方法調(diào)用的類提供并發(fā)管理。此類不能被繼承。  public static class AsyncOperationManager  {      // 獲取或設置用于異步操作的同步上下文。      public static SynchronizationContext SynchronizationContext { get; set; }         // 返回可用于對特定異步操作的持續(xù)時間進行跟蹤的AsyncOperation對象。      // 參數(shù):userSuppliedState:      //     一個對象,用于使一個客戶端狀態(tài)(如任務 ID)與一個特定異步操作相關(guān)聯(lián)。      public static AsyncOperation CreateOperation(object userSuppliedState)      {          return AsyncOperation.CreateOperation(userSuppliedState,SynchronizationContext);      }  }     // 跟蹤異步操作的生存期。  public sealed class AsyncOperation  {      // 構(gòu)造函數(shù)      private AsyncOperation(object userSuppliedState, SynchronizationContext syncContext);      internal static AsyncOperation CreateOperation(object userSuppliedState                                              , SynchronizationContext syncContext);         // 獲取傳遞給構(gòu)造函數(shù)的SynchronizationContext對象。      public SynchronizationContext SynchronizationContext { get; }      // 獲取或設置用于唯一標識異步操作的對象。      public object UserSuppliedState { get; }         // 在各種應用程序模型適合的線程或上下文中調(diào)用委托。      public void Post(SendOrPostCallback d, object arg);      // 結(jié)束異步操作的生存期。      public void OperationCompleted();      // 效果同調(diào)用 Post() + OperationCompleted() 方法組合      public void PostOperationCompleted(SendOrPostCallback d, object arg);  }

先分析下這兩個幫助器類:

1. AsyncOperationManager是靜態(tài)類。靜態(tài)類是密封的,因此不可被繼承。倘若從靜態(tài)類繼承會報錯“靜態(tài)類必須從 Object 派生”。(小常識,以前以為密封類就是 sealed 關(guān)鍵字)

2. AsyncOperationManager為支持異步方法調(diào)用的類提供并發(fā)管理,該類可正常運行于 .NET Framework 支持的所有應用程序模式下。

3. AsyncOperation實例提供對特定異步任務的生存期進行跟蹤??捎脕硖幚砣蝿胀瓿赏ㄖ€可用于在不終止異步操作的情況下發(fā)布進度報告和增量結(jié)果(這種不終止異步操作的處理是通過AsyncOperation的 Post() 方法實現(xiàn))。

4. AsyncOperation類有一個私有的構(gòu)造函數(shù)和一個內(nèi)部CreateOperation() 靜態(tài)方法。由AsyncOperationManager類調(diào)用AsyncOperation.CreateOperation() 靜態(tài)方法來創(chuàng)建AsyncOperation實例。

5. AsyncOperation類是通過SynchronizationContext類來實現(xiàn)在各種應用程序的適當“線程或上下文”調(diào)用客戶端事件處理程序。

// 提供在各種同步模型中傳播同步上下文的基本功能。  public class SynchronizationContext  {      // 獲取當前線程的同步上下文。      public static SynchronizationContext Current { get; }         // 當在派生類中重寫時,響應操作已開始的通知。      public virtual void OperationStarted();      // 當在派生類中重寫時,將異步消息調(diào)度到一個同步上下文。      public virtual void Post(SendOrPostCallback d, object state);      // 當在派生類中重寫時,響應操作已完成的通知。      public virtual void OperationCompleted();      ……  }

a) 在AsyncOperation構(gòu)造函數(shù)中調(diào)用SynchronizationContext的OperationStarted() ;

b) 在AsyncOperation的 Post() 方法中調(diào)用SynchronizationContext的Post() ;

c) 在AsyncOperation的OperationCompleted()方法中調(diào)用SynchronizationContext的OperationCompleted();

6. SendOrPostCallback委托簽名:

// 表示在消息即將被調(diào)度到同步上下文時要調(diào)用的方法。  public delegate void SendOrPostCallback(object state);

基于事件的異步模式的特征

1. 基于事件的異步模式可以采用多種形式,具體取決于某個特定類支持操作的復雜程度:

1) 最簡單的類可能只有一個 ***Async方法和一個對應的 ***Completed 事件,以及這些方法的同步版本。

2) 復雜的類可能有若干個 ***Async方法,每種方法都有一個對應的 ***Completed 事件,以及這些方法的同步版本。

3) 更復雜的類還可能為每個異步方法支持取消(CancelAsync()方法)、進度報告和增量結(jié)果(ReportProgress() 方法+ProgressChanged事件)。

4) 如果您的類支持多個異步方法,每個異步方法返回不同類型的數(shù)據(jù),您應該:

a) 將您的增量結(jié)果報告與您的進度報告分開。

b) 使用適當?shù)腅ventArgs為每個異步方法定義一個單獨的 ***ProgressChanged事件以處理該方法的增量結(jié)果數(shù)據(jù)。

5) 如果類不支持多個并發(fā)調(diào)用,請考慮公開IsBusy屬性。

6) 如要異步操作的同步版本中有 Out 和 Ref 參數(shù),它們應做為對應 ***CompletedEventArgs的一部分,eg:

public int MethodName(string arg1, ref string arg2, out string arg3);     public void MethodNameAsync(string arg1, string arg2);  public class MethodNameCompletedEventArgs : AsyncCompletedEventArgs  {      public int Result { get; };      public string Arg2 { get; };      public string Arg3 { get; };  }

2. 如果你的組件要支持多個異步耗時的任務并行執(zhí)行。那么:

1) 為***Async方法多添加一個userState對象參數(shù)(此參數(shù)應當始終是***Async方法簽名中的最后一個參數(shù)),用于跟蹤各個操作的生存期。

2) 注意要在你構(gòu)建的異步類中維護一個userState對象的集合。使用 lock 區(qū)域保護此集合,因為各種調(diào)用都會在此集合中添加和移除userState對象。

3) 在***Async方法開始時調(diào)用AsyncOperationManager.CreateOperation并傳入userState對象,為每個異步任務創(chuàng)建AsyncOperation對象,userState存儲在AsyncOperation的UserSuppliedState屬性中。在構(gòu)建的異步類中使用該屬性標識取消的操作,并傳遞給CompletedEventArgs和ProgressChangedEventArgs參數(shù)的UserState屬性來標識當前引發(fā)進度或完成事件的特定異步任務。

4) 當對應于此userState對象的任務引發(fā)完成事件時,你構(gòu)建的異步類應將AsyncCompletedEventArgs.UserState對象從集合中刪除。

3. 注意:

1) 確保 ***EventArgs類特定于***方法。即當使用 ***EventArgs類時,切勿要求開發(fā)人員強制轉(zhuǎn)換類型值。

2) 確保始終引發(fā)方法名稱Completed 事件。成功完成、異常或者取消時應引發(fā)此事件。任何情況下,應用程序都不應遇到這樣的情況:應用程序保持空閑狀態(tài),而操作卻一直不能完成。

3) 確??梢圆东@異步操作中發(fā)生的任何異常并將捕獲的異常指派給 Error 屬性。

4) 確保 ***CompletedEventArgs 類將其成員公開為只讀屬性而不是字段,因為字段會阻止數(shù)據(jù)綁定。eg:public MyReturnType Result { get; }

5) 在構(gòu)建 ***CompletedEventArgs 類屬性時,通過this.RaiseExceptionIfNecessary() 方法確保屬性值被正確使用。Eg:

public int MethodName(string arg1, ref string arg2, out string arg3);     public void MethodNameAsync(string arg1, string arg2);  public class MethodNameCompletedEventArgs : AsyncCompletedEventArgs  {      public int Result { get; };      public string Arg2 { get; };      public string Arg3 { get; };  }

2. 如果你的組件要支持多個異步耗時的任務并行執(zhí)行。那么:

1) 為***Async方法多添加一個userState對象參數(shù)(此參數(shù)應當始終是***Async方法簽名中的最后一個參數(shù)),用于跟蹤各個操作的生存期。

2) 注意要在你構(gòu)建的異步類中維護一個userState對象的集合。使用 lock 區(qū)域保護此集合,因為各種調(diào)用都會在此集合中添加和移除userState對象。

3) 在***Async方法開始時調(diào)用AsyncOperationManager.CreateOperation并傳入userState對象,為每個異步任務創(chuàng)建AsyncOperation對象,userState存儲在AsyncOperation的UserSuppliedState屬性中。在構(gòu)建的異步類中使用該屬性標識取消的操作,并傳遞給CompletedEventArgs和ProgressChangedEventArgs參數(shù)的UserState屬性來標識當前引發(fā)進度或完成事件的特定異步任務。

4) 當對應于此userState對象的任務引發(fā)完成事件時,你構(gòu)建的異步類應將AsyncCompletedEventArgs.UserState對象從集合中刪除。

3. 注意:

1) 確保 ***EventArgs類特定于***方法。即當使用 ***EventArgs類時,切勿要求開發(fā)人員強制轉(zhuǎn)換類型值。

2) 確保始終引發(fā)方法名稱Completed 事件。成功完成、異?;蛘呷∠麜r應引發(fā)此事件。任何情況下,應用程序都不應遇到這樣的情況:應用程序保持空閑狀態(tài),而操作卻一直不能完成。

3) 確??梢圆东@異步操作中發(fā)生的任何異常并將捕獲的異常指派給 Error 屬性。

4) 確保 ***CompletedEventArgs 類將其成員公開為只讀屬性而不是字段,因為字段會阻止數(shù)據(jù)綁定。eg:public MyReturnType Result { get; }

5) 在構(gòu)建 ***CompletedEventArgs 類屬性時,通過this.RaiseExceptionIfNecessary() 方法確保屬性值被正確使用。Eg:

private bool isPrimeValue;  public bool IsPrime  {      get      {          RaiseExceptionIfNecessary();          return isPrimeValue;      }  }

所以,在***Completed事件處理程序中,應當總是先檢查 ***CompletedEventArgs.Error 和 ***CompletedEventArgs.Cancelled 屬性,然后再訪問RunWorkerCompletedEventArgs.Result屬性。

BackgroundWorker組件

System.ComponentModel命名空間的BackgroundWorker組件為我們提供了一個簡單的多線程應用解決方案,它允許你在單獨的線程上運行耗時操作而不會導致用戶界面的阻塞。但是,要注意它同一時刻只能運行一個異步耗時操作(使用IsBusy屬性判定),并且不能跨AppDomain邊界進行封送處理(不能在多個AppDomain中執(zhí)行多線程操作)。

1. BackgroundWorker組件

public class BackgroundWorker : Component  {      public BackgroundWorker();         // 獲取一個值,指示應用程序是否已請求取消后臺操作。      public bool CancellationPending { get; }      // 獲取一個值,指示BackgroundWorker是否正在運行異步操作。      public bool IsBusy { get; }      // 獲取或設置一個值,該值指示BackgroundWorker能否報告進度更新。      public bool WorkerReportsProgress { get; set; }      // 獲取或設置一個值,該值指示BackgroundWorker是否支持異步取消。      public bool WorkerSupportsCancellation { get; set; }         // 調(diào)用RunWorkerAsync() 時發(fā)生。      public event DoWorkEventHandlerDoWork;      // 調(diào)用ReportProgress(System.Int32) 時發(fā)生。      public event ProgressChangedEventHandlerProgressChanged;      // 當后臺操作已完成、被取消或引發(fā)異常時發(fā)生。      public event RunWorkerCompletedEventHandlerRunWorkerCompleted;         // 請求取消掛起的后臺操作。      public void CancelAsync();      // 引發(fā)ProgressChanged事件。percentProgress:范圍從 0% 到 100%      public void ReportProgress(int percentProgress);      // userState:傳遞到RunWorkerAsync(System.Object) 的狀態(tài)對象。      public void ReportProgress(int percentProgress, object userState);      // 開始執(zhí)行后臺操作。      public void RunWorkerAsync();      // 開始執(zhí)行后臺操作。argument:傳遞給DoWork事件的DoWorkEventArgs參數(shù)。      public void RunWorkerAsync(object argument);  }

2. 相應的EventArgs類

///1)   System.EventArgs基類      // System.EventArgs是包含事件數(shù)據(jù)的類的基類。      public class EventArgs      {          // 表示沒有事件數(shù)據(jù)的事件。          public static readonly EventArgs Empty;          public EventArgs();      }     ///2)   DoWorkEventArgs類      // 為可取消的事件提供數(shù)據(jù)。      public class CancelEventArgs : EventArgs      {          public CancelEventArgs();          public CancelEventArgs(bool cancel);          // 獲取或設置指示是否應取消事件的值。          public bool Cancel { get; set; }      }      // 為DoWork事件處理程序提供數(shù)據(jù)。      public class DoWorkEventArgs : CancelEventArgs      {          public DoWorkEventArgs(object argument);             // 獲取表示異步操作參數(shù)的值。          public object Argument { get; }          // 獲取或設置表示異步操作結(jié)果的值。          public object Result { get; set; }      }     ///3)   ProgressChangedEventArgs類      // 為ProgressChanged事件提供數(shù)據(jù)。      public class ProgressChangedEventArgs : EventArgs      {          public ProgressChangedEventArgs(int progressPercentage, object userState);             // 獲取異步任務的進度百分比。          public int ProgressPercentage { get; }          // 獲取唯一的用戶狀態(tài)。          public object UserState { get; }      }     ///4)   RunWorkerCompletedEventArgs類      // 為MethodNameCompleted事件提供數(shù)據(jù)。      public class AsyncCompletedEventArgs : EventArgs      {          public AsyncCompletedEventArgs();          public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState);             // 獲取一個值,該值指示異步操作是否已被取消。          public bool Cancelled { get; }          // 獲取一個值,該值指示異步操作期間發(fā)生的錯誤。          public Exception Error { get; }          // 獲取異步任務的唯一標識符。          public object UserState { get; }             // 訪問 AsyncCompletedEventArgs 及其派生類的屬性前調(diào)用此方法          protected void RaiseExceptionIfNecessary()          {              if (this.Error != null)              {                  throw new TargetInvocationException(……);              }              if (this.Cancelled)              {                  throw new InvalidOperationException(……);              }          }      }      public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs      {          public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled);             // 獲取表示異步操作結(jié)果的值。          public object Result { get; }          // 獲取表示用戶狀態(tài)的值。          public object UserState { get; }      }

3.   BackgroundWorker示例

示例代碼中包含了BackgroundWorker源代碼及對應的使用示例,這里不粘貼代碼了,會導致篇幅更大。來個示例截圖吧:

基于事件的異步編程模式EMP如何理解

示例分析:

1)   首先我們?yōu)锽ackgroundWorker組件注冊DoWork(異步操作)、ProgressChanged(進度報告) 和RunWorkCompleted(完成通知)事件;

2)   設置WorkerSupportsCancellation和WorkerReportsProgress屬性為true,以聲明組件支持取消操作和進度報告;

3)   使用RunWorkerAsync() 開啟異步操作,通過IsBusy屬性判斷是否已經(jīng)有異步任務在執(zhí)行;

4)   使用CancelAsync() 方法取消異步操作,但要注意:

a)   它僅僅是將BackgroudWorker.CancellationPending屬性設置為true。需要在具體DoWork事件中不斷檢查BackgroudWorker.CancellationPending來設置DoWorkEventArgs的Cancel屬性。

b)   DoWork事件處理程序中的代碼有可能在發(fā)出取消請求時完成其工作,輪詢循環(huán)可能會錯過設置為 true 的CancellationPending屬性。在這種情況下,即使發(fā)出了取消請求,RunWorkerCompleted事件處理程序中RunWorkerCompletedEventArgs的 Cancelled 標志也不會設置為 true。這種情況被稱作爭用狀態(tài)。(可以通過直接監(jiān)控組件的CancellationPending屬性,來做判斷)

5)   確保在DoWork事件處理程序中不操作任何用戶界面對象。而應該通過ProgressChanged和RunWorkerCompleted事件與用戶界面進行通信。

因為RunWorkerAsync() 是通過委托的BeginInvoke() 引發(fā)的DoWork事件,即DoWork事件的執(zhí)行線程已不是創(chuàng)建控件的線程(我在《異步編程:異步編程模型 (APM)》中介紹了幾種夸線程訪問控件的方式)。而ProgressChanged和RunWorkerCompleted事件是通過幫助器類AsyncOperation的 Post() 方法使其調(diào)用發(fā)生在合適的“線程或上下文”中。

自定義基于事件的異步組件

剛才我們介紹了BackgroundWorker組件,但是這個組件在一個時刻只能開啟一個異步操作,那如果我們要想同時支持多個異步操作、進度報告、增量結(jié)果、取消和返回結(jié)果值或異常信息該怎么辦呢?對的,我們可以為自己定義一個基于事件的異步組件。

我直接引用MSDN上的一則計算質(zhì)數(shù)的異步組件示例,請從我提供的示例代碼中獲取。

質(zhì)數(shù)算法:埃拉托色尼篩法

eg:判斷n是否為質(zhì)數(shù)

1、1和0既非素數(shù)也非合數(shù);

2、將2和3加入質(zhì)數(shù)集合primes;從n=5開始,通過 n+=2 來跳過所有偶數(shù);

3、循環(huán)集合primes中的質(zhì)數(shù)并將其做為n的因子,能整除的為合數(shù);

4、若不能整除,則繼續(xù)循步驟3直到“因子的平方>n”,即可判斷n為質(zhì)數(shù),并將其加入到集合primes。


 

來個示例截圖吧:

基于事件的異步編程模式EMP如何理解

示例分析:(組件名:PrimeNumberCalculator)

1.   首先我們?yōu)镻rimeNumberCalculator組件注冊ProgressChanged(進度報告) 和CalculatePrimeCompleted(完成通知)事件;

2.   使用CalculatePrimeAsync(intnumberToTest, object taskId)開啟異步任務,注意我們需要傳遞一個唯一標識Guid taskId = Guid.NewGuid();用于標識取消的操作,并傳遞給CompletedEventArgs和ProgressChangedEventArgs參數(shù)的UserState屬性來標識當前引發(fā)進度或完成事件的特定異步任務;

3.   取消操作CancelAsync(object taskId),只是將taskId對應的AsyncOperation實例移除內(nèi)部任務集合,耗時操作通過判斷taskId是否存在于集合來判斷其是否被取消;

關(guān)于基于事件的異步編程模式EMP如何理解問題的解答就分享到這里了,希望以上內(nèi)容可以對大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識。

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。

emp
AI