同時執行許多工作但仍能回應使用者互動的應用程式,通常需要使用多個線程的設計。 命名空間 System.Threading 提供建立高效能多線程應用程式所需的所有工具,但實際上使用這些工具需要多線程軟體工程的顯著經驗。 對於相對簡單的多線程應用程式, BackgroundWorker 元件會提供簡單的解決方案。 針對更複雜的異步應用程式,請考慮實作遵守事件架構異步模式的類別。
事件架構異步模式提供多線程應用程式的優點,同時隱藏多線程設計固有的許多複雜問題。 使用支援此模式的類別,可讓您:
執行耗時的工作,例如下載和資料庫作業「在背景」,而不會中斷您的應用程式。
同時執行多個作業,在每次完成時接收通知。
等待資源變成可用,而不停止 (“blocking”) 您的應用程式。
使用熟悉的事件和委託模型,與待處理的異步操作進行通信。 如需使用事件處理程式和委派的詳細資訊,請參閱 事件。
支援事件架構異步模式的類別將會有一或多個名為 MethodNameAsync 的方法。 這些方法可能與同步版本相似,這些版本會在當前的執行緒上執行相同的操作。 類別也可能有 MethodNameCompleted 事件,而且可能有 MethodNameAsyncCancel (或只是 CancelAsync) 方法。
PictureBox 是支援事件架構異步模式的一般元件。 您可以藉由呼叫其 Load 方法以同步方式下載映像,但如果映像很大,或網路連線速度緩慢,您的應用程式將會停止回應,直到下載作業完成且呼叫傳 Load 回為止。
如果您想要讓應用程式在載入映射時繼續執行,您可以呼叫 LoadAsync 方法並處理 LoadCompleted 事件,就像處理任何其他事件一樣。 當您呼叫 LoadAsync 方法時,下載將在不同的線程上執行,而您的應用程式會繼續運行(「在背景中」)。 當映像載入作業完成時,將會呼叫事件處理程式,而事件處理程式可以檢查 AsyncCompletedEventArgs 參數,以判斷下載是否順利完成。
事件架構異步模式要求可以取消異步操作,且PictureBox 控件透過其 CancelAsync 方法支援此需求。 呼叫 CancelAsync 會提交停止正在等待的下載的請求,並且在取消工作時會觸發 LoadCompleted 事件。
謹慎
下載可能會在 CancelAsync 發出請求的同時完成,因此 Cancelled 可能無法反映取消的請求。 這稱為 競爭條件 ,在多線程程序設計中是常見的問題。 如需多線程程式設計中問題的詳細資訊,請參閱 Managed線程最佳做法。
事件為基礎的異步模式的特性
事件架構異步模式可能會採用數種形式,視特定類別所支援的作業複雜度而定。 最簡單的類別可能會有單一 MethodNameAsync 方法和對應的 MethodNameCompleted 事件。 更複雜的類別可能有數個 MethodName異步 方法,每個方法都有對應的 MethodNameCompleted 事件,以及這些方法的同步版本。 類別可以選擇性地支援每個異步方法的取消、進度報告和累加結果。
異步方法也可以支援多個擱置呼叫(多個並行調用),讓您的程式代碼在完成其他擱置作業之前多次呼叫它。 正確處理這種情況可能需要您的應用程式追蹤每個作業的完成。
事件驅動異步模式的範例
SoundPlayer和 PictureBox 元件代表事件架構異步模式的簡單實作。 WebClient和 BackgroundWorker 元件代表事件架構異步模式更複雜的實作。
以下是符合模式的範例類別宣告:
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 類別有兩種方法,兩種方法都支援同步和異步調用。 同步重載的行為如同任意方法的呼叫,在呼叫的執行緒上執行操作;如果操作非常耗時,在呼叫返回之前可能會有顯著的延遲。 異步多載方法會在另一個執行緒上啟動作業,然後立即返回,讓呼叫執行緒在作業在「背景中」執行時繼續。
異步方法重載
異步操作潛在可有兩種重載:單次調用和多次調用。 您可以藉由方法簽名來區分這兩種形式:多重調用形式具有名為 userState 的額外參數。 此窗體可讓您的程式代碼多次呼叫 Method1Async(string param, object userState) ,而不需要等待任何未完成的異步操作完成。 另一方面,如果您嘗試在先前的調用完成之前調用Method1Async(string param),方法會引發InvalidOperationException。
userState參數可讓您區分多重呼叫重載中的異步操作。 您可以為每個呼叫 Method1Async(string param, object userState)提供唯一值(例如 GUID 或哈希碼),而且當每個作業完成時,事件處理程式可以判斷作業引發完成事件的實例。
追蹤待處理作業
如果您使用多次調用過載,您的程式碼必須追蹤 userState 物件(工作ID),以管理待執行的任務。 針對每個 Method1Async(string param, object userState) 的呼叫,您通常會產生新的唯一 userState 物件,並將它新增至集合。 當對應至此 userState 物件的工作引發完成事件時,完成方法實作將會檢查 AsyncCompletedEventArgs.UserState 並從集合中移除它。 使用這種方式時,userState 參數充當任務 ID 的角色。
備註
您必須小心,在呼叫多重覆載時提供唯一的值 userState 。 非唯一 ArgumentException的工作識別碼會導致異步類別擲回 。
取消擱置作業
能夠在完成之前隨時取消異步操作是很重要的。 實作事件驅動非同步模式的類別將會有一個方法(如果只有一個 CancelAsync 非同步方法)或 MethodNameAsyncCancel 方法(如果有多個非同步方法)。
允許多個調用的方法會採用 userState 參數,可用來追蹤每個工作的存留期。
CancelAsync 會採用 userState 參數,可讓您取消特定的擱置中工作。
一次只支援單一暫止作業的方法,例如 Method1Async(string param),無法取消。
接收進度更新和累加結果
遵守事件架構異步模式的類別可能會選擇性地提供事件來追蹤進度和累加結果。 這通常會命名為 ProgressChanged 或 MethodNameProgressChanged,且其對應的事件處理常式會接受 ProgressChangedEventArgs 參數。
事件 ProgressChanged 的事件處理程式可以檢查屬性 ProgressChangedEventArgs.ProgressPercentage,以判斷非同步工作已完成的百分比。 這個屬性的範圍從 0 到 100,而且可以用來更新 Value 的 ProgressBar屬性。 如果有多個異步作業正在進行中,您可以使用 ProgressChangedEventArgs.UserState 屬性來區分哪個作業正在報告進度。
某些類別可能會在進行異步操作時回報漸進結果。 這些結果會儲存在衍生自 ProgressChangedEventArgs 的類別中,而且它們會顯示為衍生類別中的屬性。 您可以在事件的事件處理程式 ProgressChanged 中存取這些結果,就像存取 ProgressPercentage 屬性一樣。 如果有多個異步操作正在等待,您可以使用 UserState 屬性來區分正在報告累加結果的操作。