共用方式為


實作事件驅動非同步模式

如果您撰寫的類別中包含可能引起明顯延遲的操作,請考慮透過實作事件驅動異步模式來賦予異步功能。

事件架構異步模式提供標準化的方式來封裝具有異步功能的類別。 如果使用 之類的 AsyncOperationManager協助程式類別實作,您的類別在任何應用程式模型下都會正常運作,包括 ASP.NET、控制台應用程式和 Windows Forms 應用程式。

如需實作事件架構異步模式的範例,請參閱 如何:實作支援事件架構異步模式的元件

對於簡單的異步操作,您可能會找到適合的 BackgroundWorker 元件。 如需有關 BackgroundWorker 的更多資訊,請參閱 如何在背景中執行作業

下列清單描述本主題所討論事件架構異步模式的功能。

  • 實作事件驅動的異步模式的機會

  • 命名異步方法

  • 選擇性地支援取消

  • 選擇性地支援IsBusy屬性

  • 可選擇地提供進度報告支援

  • 選擇性地提供傳回累加結果的支援

  • 處理方法中的 Out 和 Ref 參數

實作基於事件的異步模式的機會

請考慮在下列情況下實作事件架構異步模式:

  • 您的類別的客戶不需要 WaitHandleIAsyncResult 物件來進行異步操作,這意味著客戶需要自行建立輪詢以及 WaitAllWaitAny

  • 您希望用戶端透過熟悉的事件/委派模型來管理異步操作。

任何作業都是異步實作的候選專案,但您應該考慮您預期會產生長時間延遲的作業。 特別適合的操作是客戶端呼叫方法並在完成時收到通知,而不需要進一步的介入。 此外,還有持續執行的作業,定期通知用戶端進度、累加結果或狀態變更。

如需決定何時支援事件架構異步模式的詳細資訊,請參閱 決定何時實作事件架構異步模式

命名異步方法

針對您想要提供異步版本的每個同步方法 MethodName

定義 MethodName異步 方法,以:

  • 傳回 void

  • 接受與 MethodName 方法相同的參數。

  • 接受多次呼叫。

選擇性地定義 MethodNameAsync 多載,與 MethodNameAsync 相同,但具有稱為 userState的其他物件值參數。 如果您已準備好管理方法的多個並行調用,在此情況下, userState 值會傳回給所有事件處理程式,以區分方法的調用。 您也可以選擇只做為儲存用戶狀態的位置,以供稍後擷取。

針對每個個別 MethodNameAsync 方法簽章:

  1. 在與 方法相同的類別中定義下列事件:

    Public Event MethodNameCompleted As MethodNameCompletedEventHandler
    
    public event MethodNameCompletedEventHandler MethodNameCompleted;
    
  2. 定義下列委派和 AsyncCompletedEventArgs。 這些可能會定義在類別本身之外,但在相同的命名空間中。

    Public Delegate Sub MethodNameCompletedEventHandler( _
        ByVal sender As Object, _
        ByVal e As MethodNameCompletedEventArgs)
    
    Public Class MethodNameCompletedEventArgs
        Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As MyReturnType
    End Property
    
    public delegate void MethodNameCompletedEventHandler(object sender,
        MethodNameCompletedEventArgs e);
    
    public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
    {
        public MyReturnType Result { get; }
    }
    
    • 確定 MethodNameCompletedEventArgs 類別會將其成員公開為只讀屬性,而不是字段,因為字段會防止數據系結。

    • 請勿針對不會產生結果的方法定義任何 AsyncCompletedEventArgs衍生類別。 只要使用本身的 AsyncCompletedEventArgs 實例即可。

      備註

      當可行且適當時,重複使用委派和 AsyncCompletedEventArgs 類型是完全可接受的。 在此情況下,命名將無法與方法名稱完全一致,因為指定的委派和 AsyncCompletedEventArgs 不會繫結至單一方法。

選擇性地支援取消

如果您的類別支援取消異步操作,應該如以下所述將取消機制公開給客戶端。 定義取消支援之前,必須先達成兩個決策點:

  • 您的類別,包括未來預期會加入的項目,是否只有一個支援取消的異步操作?
  • 支援取消的異步操作是否能夠支援多個未完成的操作? 也就是說,MethodNameAsync 方法是否接受 userState 參數,並且是否允許多次調用而不必等候其中任何一次完成?

使用下表中這兩個問題的解答來判斷取消方法的簽章應該是什麼。

Visual Basic

支援多個同時作業 一次只有一個作業
整個類別中的一個異步作業 Sub MethodNameAsyncCancel(ByVal userState As Object) Sub MethodNameAsyncCancel()
類別中的多個異步作業 Sub CancelAsync(ByVal userState As Object) Sub CancelAsync()

C#

支援多個同時作業 一次僅可執行一個作業
整個類別中的一個異步作業 void MethodNameAsyncCancel(object userState); void MethodNameAsyncCancel();
類別中的多個異步作業 void CancelAsync(object userState); void CancelAsync();

如果您定義 CancelAsync(object userState) 方法,則用戶端在選擇其狀態值時必須小心,使其能夠區分物件上叫用的所有異步方法,而不只是在單一異步方法的所有調用之間。

將單一異步作版本 MethodNameAsyncCancel 命名的決定是以能夠在 Visual Studio 的 IntelliSense 等設計環境中更輕鬆地探索方法為基礎。 這會將相關成員分組,並將它們與與異步功能無關的其他成員區別開來。 如果您預期在後續版本中可能會新增其他異步操作,最好定義 CancelAsync

請勿在相同類別中從上述數據表定義多個方法。 這樣做會沒有意義,或會因為方法數量過多而使類別介面顯得凌亂。

這些方法通常會立即返回,且作業不一定會實際取消。 在 MethodNameCompleted 事件的事件處理程式中, MethodNameCompletedEventArgs 物件包含字段 Cancelled ,用戶端可用來判斷是否發生取消。

請遵守 事件架構異步模式最佳做法中所述的取消的語意。

選擇性地支援IsBusy屬性

如果您的類別不支援多個並行調用,請考慮公開 IsBusy 屬性。 這可讓開發人員判斷 MethodNameAsync 方法是否正在執行,而不擷取 MethodNameAsync 方法的例外狀況。

遵守IsBusy實作事件架構異步模式之最佳做法中所述的語意

可選擇提供進度報告支援

異步作業常常需要在操作期間報告進度。 事件架構異步模式提供執行此動作的指導方針。

  • 選擇性地定義異步作所引發的事件,並在適當的線程上叫用。 物件 ProgressChangedEventArgs 會攜帶整數值進度指標,預期介於 0 到 100 之間。

  • 將此事件命名如下:

    • ProgressChanged 如果類別有多個異步操作(或預期會在未來版本中成長為包含多個異步操作):

    • 如果類別具有單一非同步操作,則 MethodNameProgressChanged

    這個命名選擇類似於取消方法的命名,如選擇性支援取消一節中所述。

此事件應該使用 ProgressChangedEventHandler 委派簽章和 ProgressChangedEventArgs 類別。 或者,如果可以提供更專域的進度指標(例如,有關下載作業的已讀取位元組和總位元組),那麼您應該定義一個ProgressChangedEventArgs的衍生類別。

請注意,類別只有一個 ProgressChangedMethodNameProgressChanged 事件,不論其支援的異步方法數目為何。 客戶端應該使用 userState 傳遞至 MethodNameAsync 方法的物件,以區分多個並行作業上的進度更新。

在某些情況下,有多個作業支持進度,而且每個作業都會傳回不同的進度指標。 在此情況下,單 ProgressChanged 一事件並不適用,而且您可以考慮支援多個 ProgressChanged 事件。 在此情況下,請針對每個 MethodNameAsync 方法使用 MethodNameProgressChanged 的命名模式。

遵守實作 事件架構異步模式的最佳做法中所述的進度報告語意。

可選擇地提供返回增量結果的支持

有時候,異步操作可以在完成之前傳回階段性結果。 有許多選項可用來支援此案例。 以下是一些範例。

單一操作類別

如果您的類別只支援單一異步作,且該作業能夠傳回累加結果,則:

  • 擴充ProgressChangedEventArgs類型以攜帶增量結果數據,並使用這個擴充數據來定義MethodNameProgressChanged事件。

  • 當有累加結果要回報時,觸發這個 MethodNameProgressChanged 事件。

此解決方案僅特別適用於單一異步操作類別,因為當相同事件發生於「所有操作」時,如 MethodNameProgressChanged 事件,返回逐步結果並不會有問題。

具有同質累加結果的多重操作類別

在此情況下,您的類別支援多個異步方法,每個方法都能夠傳回累加結果,而這些累加結果全都有相同的數據類型。

遵循上述單一作業類別的模型,因為相同的 EventArgs 結構適用於所有累加結果。 定義一個ProgressChanged事件,而不是定義MethodNameProgressChanged事件,因為它適用於多個異步方法。

具有異質增量結果的多作業類別

如果您的類別支援多個異步方法,則每個方法都會傳回不同類型的數據,您應該:

  • 將累加結果報告與進度報告分開。

  • 定義每個異步方法的個別 MethodNameProgressChanged 事件,以適當處理該方法的累加結果數據。

叫用適當線程上的事件處理程式,如 實作事件架構異步模式的最佳做法中所述。

處理方法中的 Out 和 Ref 參數

雖然在 .NET 中通常不建議使用 outref,但以下是在這些元素存在時要遵循的規則:

指定同步方法 MethodName

  • out MethodName 的參數不應該是 MethodNameAsync 的一部分。 相反地,它們應該是 MethodNameCompletedEventArgs 的一部分,其名稱與 MethodName 中的參數對等名稱相同(除非有更適當的名稱)。

  • ref MethodName 的參數應該會顯示為 MethodNameAsync 的一部分,而且當做 MethodNameCompletedEventArgs 的一部分,其參數與 MethodName 中對等的參數相同(除非有更適當的名稱)。

例如,假設:

Public Function MethodName(ByVal arg1 As String, ByRef arg2 As String, ByRef arg3 As String) As Integer
public int MethodName(string arg1, ref string arg2, out string arg3);

您的異步方法及其 AsyncCompletedEventArgs 類別看起來會像這樣:

Public Sub MethodNameAsync(ByVal arg1 As String, ByVal arg2 As String)

Public Class MethodNameCompletedEventArgs
    Inherits System.ComponentModel.AsyncCompletedEventArgs
    Public ReadOnly Property Result() As Integer
    End Property
    Public ReadOnly Property Arg2() As String
    End Property
    Public ReadOnly Property Arg3() As String
    End Property
End Class
public void MethodNameAsync(string arg1, string arg2);

public class MethodNameCompletedEventArgs : System.ComponentModel.AsyncCompletedEventArgs
{
    public int Result { get; };
    public string Arg2 { get; };
    public string Arg3 { get; };
}

另請參閱