事件架構非同步模式概觀Event-based Asynchronous Pattern Overview

要同時執行許多工作,還能繼續回應使用者互動,這樣的應用程式通常都需要可以使用多執行緒的設計。Applications that perform many tasks simultaneously, yet remain responsive to user interaction, often require a design that uses multiple threads. System.Threading 命名空間提供建立高效能多執行緒應用程式的所有必要工具,但是要有效地使用這些工具,需要具備多執行緒軟體工程的豐富經驗。The System.Threading namespace provides all the tools necessary to create high-performance multithreaded applications, but using these tools effectively requires significant experience with multithreaded software engineering. 對於較簡單的多執行緒應用程式,BackgroundWorker 元件提供了簡單明瞭的方案。For relatively simple multithreaded applications, the BackgroundWorker component provides a straightforward solution. 如果是較為複雜精細的非同步應用程式,請考慮實作遵守事件架構非同步模式的類別。For more sophisticated asynchronous applications, consider implementing a class that adheres to the Event-based Asynchronous Pattern.

事件架構非同步模式提供多執行緒應用程式的優點,同時隱藏多執行緒設計中許多原有的複雜問題。The Event-based Asynchronous Pattern makes available the advantages of multithreaded applications while hiding many of the complex issues inherent in multithreaded design. 使用支援此模式的類別,可以讓您:Using a class that supports this pattern can allow you to:

  • 「在背景中」執行耗時的工作,像是下載及資料庫作業,而不會中斷應用程式。Perform time-consuming tasks, such as downloads and database operations, "in the background," without interrupting your application.

  • 同時執行多項作業,並在每項作業完成時都收到通知。Execute multiple operations simultaneously, receiving notifications when each completes.

  • 等候資源變成可供使用,而不需要停止 (「封鎖」) 應用程式。Wait for resources to become available without stopping ("blocking") your application.

  • 使用熟悉的事件和委派模型,與暫止的非同步作業通訊。Communicate with pending asynchronous operations using the familiar events-and-delegates model. 如需使用事件處理常式和委派的詳細資訊,請參閱事件For more information on using event handlers and delegates, see Events.

支援事件架構非同步模式的類別,會有一或多個名為 MethodNameAsync 的方法。A class that supports the Event-based Asynchronous Pattern will have one or more methods named MethodNameAsync. 這些方法可能鏡像在目前執行緒上執行相同作業的同步版本。These methods may mirror synchronous versions, which perform the same operation on the current thread. 這個類別可能也具有 MethodNameCompleted 事件,且具有 MethodNameAsyncCancel (或簡稱 CancelAsync) 方法。The class may also have a MethodNameCompleted event and it may have a MethodNameAsyncCancel (or simply CancelAsync) method.

PictureBox 是支援事件架構非同步模式的一般元件。PictureBox is a typical component that supports the Event-based Asynchronous Pattern. 您可以呼叫影像的 Load 方法以同步下載影像,但如果影像非常龐大或是網路連接相當緩慢,則在下載作業完成及傳回 Load 的呼叫之前,應用程式都會是停止回應狀態。You can download an image synchronously by calling its Load method, but if the image is large, or if the network connection is slow, your application will stop responding until the download operation is completed and the call to Load returns.

如果要讓應用程式在載入影像時持續執行,您可以呼叫 LoadAsync 方法並處理 LoadCompleted 事件,就像處理其他任何事件一樣。If you want your application to keep running while the image is loading, you can call the LoadAsync method and handle the LoadCompleted event, just as you would handle any other event. 當您呼叫 LoadAsync 方法時,應用程式就會繼續執行,同時下載會在個別的執行緒上 (「在背景中」) 進行。When you call the LoadAsync method, your application will continue to run while the download proceeds on a separate thread ("in the background"). 當影像載入作業完成時,就會呼叫您的事件處理常式,此時事件處理常式便可以檢查 AsyncCompletedEventArgs 參數,以判斷下載是否順利完成。Your event handler will be called when the image-loading operation is complete, and your event handler can examine the AsyncCompletedEventArgs parameter to determine if the download completed successfully.

事件架構非同步模式需要能夠取消非同步作業,而 PictureBox 控制項以它所具有的 CancelAsync 方法支援這項需求。The Event-based Asynchronous Pattern requires that an asynchronous operation can be canceled, and the PictureBox control supports this requirement with its CancelAsync method. 呼叫 CancelAsync 便會發出要求讓暫止的下載停止,而且在取消工作時,就會引發 LoadCompleted 事件。Calling CancelAsync submits a request to stop the pending download, and when the task is canceled, the LoadCompleted event is raised.

警告

產生 CancelAsync 要求時剛好完成下載也是可能發生的,因此 Cancelled 可能無法反映取消的要求。It is possible that the download will finish just as the CancelAsync request is made, so Cancelled may not reflect the request to cancel. 這種情況稱為「競爭情形」 (Race Condition),這是多執行緒程式設計中的常見問題。This is called a race condition and is a common issue in multithreaded programming. 如需多執行緒程式設計問題的詳細資訊,請參閱受控執行緒處理的最佳做法For more information on issues in multithreaded programming, see Managed Threading Best Practices.

事件架構非同步模式的特性Characteristics of the Event-based Asynchronous Pattern

事件架構非同步模式可能會採用數種格式,需視特定類別所支援作業的複雜度而定。The Event-based Asynchronous Pattern may take several forms, depending on the complexity of the operations supported by a particular class. 最簡單的類別可以有單一的 MethodNameAsync 方法和對應的 MethodNameCompleted 事件。The simplest classes may have a single MethodNameAsync method and a corresponding MethodNameCompleted event. 比較複雜的類別則可以有數個 MethodNameAsync 方法,每一個方法都有對應的 MethodNameCompleted 事件,以及這些方法的同步版本。More complex classes may have several MethodNameAsync methods, each with a corresponding MethodNameCompleted event, as well as synchronous versions of these methods. 這些類別可以選擇性地支援每個非同步方法的取消、進度報告和累加結果。Classes can optionally support cancellation, progress reporting, and incremental results for each asynchronous method.

非同步方法也可以支援多次暫止呼叫 (多個並行引動過程),讓您的程式碼能在完成其他暫止作業之前,對這種方法呼叫任意次數。An asynchronous method may also support multiple pending calls (multiple concurrent invocations), allowing your code to call it any number of times before it completes other pending operations. 要正確地處理這種情況,您的應用程式可能需要追蹤每個作業的完成狀態。Correctly handling this situation may require your application to track the completion of each operation.

事件架構非同步模式的範例Examples of the Event-based Asynchronous Pattern

SoundPlayerPictureBox 元件代表事件架構非同步模式的簡單實作。The SoundPlayer and PictureBox components represent simple implementations of the Event-based Asynchronous Pattern. WebClientBackgroundWorker 元件則代表事件架構非同步模式的較複雜實作。The WebClient and BackgroundWorker components represent more complex implementations of the Event-based Asynchronous Pattern.

以下是符合此模式的範例類別宣告:Below is an example class declaration that conforms to the pattern:

Public Class AsyncExample  
    ' Synchronous methods.  
    Public Function Method1(ByVal param As String) As Integer   
    Public Sub Method2(ByVal param As Double)   
  
    ' Asynchronous methods.  
    Overloads Public Sub Method1Async(ByVal param As String)   
    Overloads Public Sub Method1Async(ByVal param As String, ByVal userState As Object)   
    Public Event Method1Completed As Method1CompletedEventHandler  
  
    Overloads Public Sub Method2Async(ByVal param As Double)   
    Overloads Public Sub Method2Async(ByVal param As Double, ByVal userState As Object)   
    Public Event Method2Completed As Method2CompletedEventHandler  
  
    Public Sub CancelAsync(ByVal userState As Object)   
  
    Public ReadOnly Property IsBusy () As Boolean  
  
    ' Class implementation not shown.  
End Class  
public class AsyncExample  
{  
    // Synchronous methods.  
    public int Method1(string param);  
    public void Method2(double param);  
  
    // Asynchronous methods.  
    public void Method1Async(string param);  
    public void Method1Async(string param, object userState);  
    public event Method1CompletedEventHandler Method1Completed;  
  
    public void Method2Async(double param);  
    public void Method2Async(double param, object userState);  
    public event Method2CompletedEventHandler Method2Completed;  
  
    public void CancelAsync(object userState);  
  
    public bool IsBusy { get; }  
  
    // Class implementation not shown.  
}  

虛擬 AsyncExample 類別具有兩個方法,這兩種方法都支援同步和非同步引動過程。The fictitious AsyncExample class has two methods, both of which support synchronous and asynchronous invocations. 同步多載的行為與任何方法呼叫都一樣,並且會在呼叫的執行緒上執行作業;如果作業相當耗時,在呼叫傳回之前,可能會明顯出現延遲。The synchronous overloads behave like any method call and execute the operation on the calling thread; if the operation is time-consuming, there may be a noticeable delay before the call returns. 非同步多載會在另一個執行緒上啟動作業,然後立即傳回,讓呼叫的執行緒能夠繼續進行,同時作業依然能「在背景中」執行。The asynchronous overloads will start the operation on another thread and then return immediately, allowing the calling thread to continue while the operation executes "in the background."

非同步方法多載Asynchronous Method Overloads

非同步作業具有兩種可能的多載:單一引動過程和多個引動過程。There are potentially two overloads for the asynchronous operations: single-invocation and multiple-invocation. 您可以根據其方法簽章來區別這兩種格式:多個引動過程格式具有稱為 userState 的額外參數。You can distinguish these two forms by their method signatures: the multiple-invocation form has an extra parameter called userState. 這種格式使您的程式碼能夠呼叫 Method1Async(string param, object userState) 多次,而不需要等候任何暫止的非同步作業完成。This form makes it possible for your code to call Method1Async(string param, object userState) multiple times without waiting for any pending asynchronous operations to finish. 另一方面,如果您在先前的引動過程完成之前,嘗試呼叫 Method1Async(string param),此方法就會引發 InvalidOperationExceptionIf, on the other hand, you try to call Method1Async(string param) before a previous invocation has completed, the method raises an InvalidOperationException.

多個引動過程多載的 userState 參數可讓您區別各種非同步作業。The userState parameter for the multiple-invocation overloads allows you to distinguish among asynchronous operations. 您可以為 Method1Async(string param, object userState) 的每個呼叫提供唯一值 (例如,GUID 或雜湊碼),當每個作業完成時,事件處理常式就可以判斷是哪個作業的執行個體引發了完成事件。You provide a unique value (for example, a GUID or hash code) for each call to Method1Async(string param, object userState), and when each operation is completed, your event handler can determine which instance of the operation raised the completion event.

追蹤暫止的作業Tracking Pending Operations

如果使用多個引動過程多載,程式碼就必須持續追蹤暫止工作的 userState 物件 (工作 ID)。If you use the multiple-invocation overloads, your code will need to keep track of the userState objects (task IDs) for pending tasks. 對於 Method1Async(string param, object userState) 的每個呼叫,一般都會產生新的、唯一的 userState 物件,並將該物件加入至集合中。For each call to Method1Async(string param, object userState), you will typically generate a new, unique userState object and add it to a collection. 當對應至這個 userState 物件的工作引發完成事件時,完成方法實作就會檢查 AsyncCompletedEventArgs.UserState,並將它從您的集合中移除。When the task corresponding to this userState object raises the completion event, your completion method implementation will examine AsyncCompletedEventArgs.UserState and remove it from your collection. 如果使用這種方式,userState 參數就會成為工作 ID。Used this way, the userState parameter takes the role of a task ID.

注意

在對多個引動過程多載的呼叫中,請務必謹慎地為 userState 提供唯一值。You must be careful to provide a unique value for userState in your calls to multiple-invocation overloads. 非唯一的工作 ID 將會使非同步類別擲回 ArgumentExceptionNon-unique task IDs will cause the asynchronous class throw an ArgumentException.

取消暫止的作業Canceling Pending Operations

在非同步作業完成之前,隨時都能取消這些作業是非常重要的。It is important to be able to cancel asynchronous operations at any time before their completion. 實作事件架構非同步模式的類別會具有 CancelAsync 方法 (如果只有一個非同步方法) 或 MethodNameAsyncCancel 方法 (如果有多個非同步方法)。Classes that implement the Event-based Asynchronous Pattern will have a CancelAsync method (if there is only one asynchronous method) or a MethodNameAsyncCancel method (if there are multiple asynchronous methods).

允許多個引動過程的方法使用 userState 參數,該參數可用於追蹤每個工作的存留期。Methods that allow multiple invocations take a userState parameter, which can be used to track the lifetime of each task. CancelAsync 使用 userState 參數,該參數可讓您取消特定的暫止工作。CancelAsync takes a userState parameter, which allows you to cancel particular pending tasks.

您無法取消一次只能支援一個暫止作業的方法 (例如 Method1Async(string param))。Methods that support only a single pending operation at a time, like Method1Async(string param), are not cancelable.

接收進度更新和累加結果Receiving Progress Updates and Incremental Results

遵守事件架構非同步模式的類別,可能會選擇性地提供用來追蹤進度和累加結果的事件。A class that adheres to the Event-based Asynchronous Pattern may optionally provide an event for tracking progress and incremental results. 這個事件通常會命名為 ProgressChangedMethodNameProgressChanged,且它的對應事件處理常式將會使用 ProgressChangedEventArgs 參數。This will typically be named ProgressChanged or MethodNameProgressChanged, and its corresponding event handler will take a ProgressChangedEventArgs parameter.

ProgressChanged 事件的事件處理常式可以檢查 ProgressChangedEventArgs.ProgressPercentage 屬性,以判斷已經完成非同步工作的百分比。The event handler for the ProgressChanged event can examine the ProgressChangedEventArgs.ProgressPercentage property to determine what percentage of an asynchronous task has been completed. 這個屬性的範圍會介於 0 到 100 之間,並且可以用來更新 ValueProgressBar 屬性。This property will range from 0 to 100, and it can be used to update the Value property of a ProgressBar. 如果正在暫止多個非同步作業,您可以使用 ProgressChangedEventArgs.UserState 屬性來區別哪個作業在報告進度。If multiple asynchronous operations are pending, you can use the ProgressChangedEventArgs.UserState property to distinguish which operation is reporting progress.

有些類別可以在進行非同步作業時報告累加結果。Some classes may report incremental results as asynchronous operations proceed. 這些結果將會儲存在衍生自 ProgressChangedEventArgs 的類別中,並且會顯示為衍生類別中的屬性。These results will be stored in a class that derives from ProgressChangedEventArgs and they will appear as properties in the derived class. 您可以像是存取 ProgressChanged 屬性一般,存取 ProgressPercentage 事件之事件處理常式中的這些結果。You can access these results in the event handler for the ProgressChanged event, just as you would access the ProgressPercentage property. 如果正在暫止多個非同步作業,您可以使用 UserState 屬性來區別哪個作業在報告累加結果。If multiple asynchronous operations are pending, you can use the UserState property to distinguish which operation is reporting incremental results.

另請參閱See also