MSDNweb
那些同時執行多項任務、但仍能響應用戶交互的應用程序一般須要實施一種使用多線程的設計方案。System.Threading 命名空間提供了建立高性能多線程應用程序所必需的全部工具,但要想有效地使用這些工具,須要有豐富的使用多線程軟件工程的經驗。對於相對簡單的多線程應用程序,BackgroundWorker 組件提供了一個簡單的解決方案。對於更復雜的異步應用程序,請考慮實現一個符合基於事件的異步模式的類。數據庫
基於事件的異步模式具備多線程應用程序的優勢,同時隱匿了多線程設計中固有的許多複雜問題。使用支持此模式的類,您將可以:編程
「在後臺」執行耗時任務(例以下載和數據庫操做),但不會中斷您的應用程序。windows
同時執行多個操做,每一個操做完成時都會接到通知。網絡
等待資源變得可用,但不會中止(「掛起」)您的應用程序。多線程
使用熟悉的事件和委託模型與掛起的異步操做通訊。有關使用事件處理程序和委託的更多信息,請參見事件和委託。異步
支持基於事件的異步模式的類將有一個或多個名爲 MethodNameAsync 的方法。這些方法可能會建立同步版本的鏡像,這些同步版本會在當前線程上執行相同的操做。此類還可能有一個MethodNameCompleted 事件,並且它可能會有一個 MethodNameAsyncCancel(或只是 CancelAsync)方法。async
PictureBox 是一個支持基於事件的異步模式的典型組件。您能夠經過調用其 Load 方法來同步下載圖像,可是若是圖像很大,或者網絡鏈接很慢,您的應用程序將中止(「掛起」),直到下載操做完成而且對 Load 的調用返回後纔會繼續執行。工具
若是您但願您的應用程序在加載圖像時保持運行,您能夠調用 LoadAsync 方法,處理 LoadCompleted 事件,這與您處理任何其餘事件沒有什麼兩樣。調用 LoadAsync 方法時,您的應用程序將繼續運行,而下載操做將在另外一個線程上(「在後臺」)繼續。圖像加載操做完成時,將會調用您的事件處理程序,您的事件處理程序能夠檢查 AsyncCompletedEventArgs 參數以肯定下載是否已成功完成。性能
基於事件的異步模式要求異步操做能夠取消,PictureBox 控件使用其 CancelAsync 方法來支持此要求。調用 CancelAsync 會提交一箇中止掛起的下載的請求,任務取消時會引起 LoadCompleted事件。
![]() |
---|
下載有可能恰在發出 CancelAsync 請求時完成,所以 Cancelled 可能沒有反映取消請求。這叫作「爭用條件」,是多線程編程中常見的一個問題。有關多線程編程中的問題的更多信息,請參見託管線程處理的最佳作法。 |
基於事件的異步模式能夠採用多種形式,具體取決於某個特定類支持的操做的複雜程度。最簡單的類可能只有一個 MethodNameAsync 方法和一個對應的 MethodNameCompleted 事件。更復雜的類可能有若干個 MethodNameAsync 方法(每種方法都有一個對應的 MethodNameCompleted 事件),以及這些方法的同步版本。這些類分別支持各類異步方法的取消、進度報告和增量結果。
異步方法可能還支持多個掛起的調用(多個並行調用),容許您的代碼在此方法完成其餘掛起的操做以前調用此方法任意屢次。若要正確處理此種狀況,必須讓您的應用程序可以跟蹤各個操做的完成。
SoundPlayer 和 PictureBox 組件表示基於事件的異步模式的簡單實現。WebClient 和 BackgroundWorker 組件表示基於事件的異步模式的更復雜的實現。
下面是一個符合此模式的類聲明示例:
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 對象的任務引起完成事件時,您的完成方法實現將檢查 System.ComponentModel.AsyncCompletedEventArgs.UserState 並將此對象從集合中刪除。在以這種方式使用時,userState 參數充當任務 ID 的角色。
![]() |
---|
在爲您對多調用重載的調用中的 userState 提供惟一值時,必定要當心。若是任務 ID 不惟一,將致使異步類引起 ArgumentException。 |
咱們必須可以在異步操做完成以前隨時取消它們,這一點很重要。實現基於事件的異步模式的類將有一個 CancelAsync 方法(若是有多個異步方法)或 MethodNameAsyncCancel 方法(若是隻有一個異步方法)。
容許多個調用的方法採用 userState 參數,此參數可用來跟蹤各個任務的生存期。CancelAsync 採用 userState 參數,此參數容許您取消特定的掛起任務。
一次只支持一個掛起的操做的方法(如 Method1Async(string param))是不可取消的。
符合基於事件的異步模式的類能夠爲跟蹤進度和增量結果提供事件。此事件一般叫作 ProgressChanged 或 MethodNameProgressChanged,它對應的事件處理程序會帶有一個ProgressChangedEventArgs 參數。
ProgressChanged 事件的事件處理程序能夠檢查 System.ComponentModel.ProgressChangedEventArgs.ProgressPercentage 屬性來肯定異步任務完成的百分比。此屬性的範圍是 0 到 100,可用來更新 ProgressBar 的 Value 屬性。若是有多個異步操做掛起,您可使用 System.ComponentModel.ProgressChangedEventArgs.UserState 屬性來分辨出哪一個操做在報告進度。
一些類可能會在異步操做繼續時報告增量結果。這些結果將保存的派生自 ProgressChangedEventArgs 的類中,並顯示爲此派生類中的屬性。您能夠在 ProgressChanged 事件的事件處理程序中訪問這些結果,就像訪問 ProgressPercentage 屬性同樣。若是有多個異步操做掛起,您可使用 UserState 屬性來分辨出哪一個操做在報告增量結果。