四、事件架構非同步模式(設計層面)
基於事件的C#非同步編程模式是比IAsyncResult模式更進階的一種非同步編程模式,也被用在更多的場合。該非同步模式具有以下優點:
· “在後台”執行耗時任務(例如下載和資料庫操作),但不會中斷您的應用程式。
· 同時執行多個操作,每個操作完成時都會接到通知(在通知中可以區分是完成了哪個操作)。
· 等待資源變得可用,但不會停止(“掛起”)您的應用程式。
· 使用熟悉的事件和委託模型與掛起的非同步作業通訊。
對於相對簡單的應用程式可以直接用 .Net 2.0 新增的 BackgroundWorker 組件來很方便的實現,對於更複雜的非同步應用程式則需要自己實現一個符合基於事件的C#非同步編程模式的類。在實現事件架構非同步模式的設計前,需要瞭解事件架構非同步模式的實現原理是什麼。事件架構非同步模式需要以下三個類型的協助。
AsyncOperation:提供了對非同步作業的生存期進行跟蹤的功能,包括操作進度通知和操作完成通知,並確保在正確的線程或上下文中調用用戶端的事件處理常式。
public void Post(SendOrPostCallback d,Object arg);
public void PostOperationCompleted(SendOrPostCallback d,Object arg);
通過在非同步輔助代碼中調用Post方法把進度和中間結果報告給使用者,如果是取消非同步任務或提示非同步任務已完成,則通過調用PostOperationCompleted方法結束非同步作業的跟蹤生命期。在PostOperationCompleted方法調用後,AsyncOperation對象變得不再可用,再次訪問將引發異常。在此有個問題:在該非同步模式中,通過AsyncOperation的Post函數來通知進度的時候,是如何使SendOrPostCallback委託在UI線程上執行的?針對該問題下文有具體分析。
AsyncOperationManager:為AsyncOperation對象的建立提供了便捷方式,通過CreateOperation方法可以建立多個AsyncOperation執行個體,實現對多個非同步作業進行跟蹤。
WindowsFormsSynchronizationContext:該類繼承自SynchronizationContext類型,提供 Windows 表單應用程式模型的同步上下文。該類型是基於事件非同步模式通訊的核心。之所以說該類型是基於事件非同步模式的通訊核心,是因為該類型解決了“保證SendOrPostCallback委託在UI線程上執行”的問題。它是如何解決的?請看AsyncOperation類型的Post方法的實現:
/// <summary> /// AsyncOperation類型的Post方法的實現 /// </summary> public void Post(SendOrPostCallback d, object arg) { this.VerifyNotCompleted(); this.VerifyDelegateNotNull(d); this.syncContext.Post(d, arg); }
在AsyncOperation類型的Post方法中,直接調用了SynchronizationContext類型的Post方法,再看該Post方法的實現:
/// <summary> /// WindowsFormsSynchronizationContext類型的Post方法的實現 /// </summary> public override void Post(SendOrPostCallback d, object state) { if (this.controlToSendTo != null) { this.controlToSendTo.BeginInvoke(d, new object[] { state }); //此處保證了SendOrPostCallBack委託在UI線程上執行 } }
有以上三個類型(AsyncOpertion,AsyncOperationManager和SynchronizationContext)作為基礎,實現事件架構非同步模式的進度通知和完成通知就輕鬆多了。下面用一個基於事件的非同步模型的例子來結束本文章。
using System;using System.Collections.Generic;using System.Text;using System.ComponentModel;using System.Collections.Specialized;using System.Threading;namespace test{ /// <summary> /// 任務1的進度通知代理 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public delegate void Work1ProgressChangedEventHandler(object sender, Work1ProgressChangedEventArgs e); /// <summary> /// 任務1的進度通知參數 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> public delegate void Work1CompletedEventHandler(object sender, Work1CompletedEventArgs e); public class BasedEventAsyncWorker { private delegate void WorkerEventHandler(int maxNumber, AsyncOperation asyncOp); private HybridDictionary userStateToLifetime = new HybridDictionary(); public BasedEventAsyncWorker() { } #region DoWork1的基於事件的非同步呼叫 public void DoWork1Async(object userState, int maxNumber) { AsyncOperation asyncOp = AsyncOperationManager.CreateOperation(userState); //userStateToLifetime有可能會同時被多線程訪問,在此需要lock進行同步處理 lock (userStateToLifetime.SyncRoot) { if (userStateToLifetime.Contains(userState)) { throw new ArgumentException( "userState parameter must be unique", "userState"); } userStateToLifetime[userState] = asyncOp; } //非同步開始任務1 WorkerEventHandler workerDelegate = new WorkerEventHandler(DoWork1); workerDelegate.BeginInvoke(maxNumber, asyncOp, null, null); } private void DoWork1(int maxNumber, AsyncOperation asyncOp) { Exception e = null; //判斷該userState的任務仍在處理中 if (!TaskCanceled(asyncOp.UserSuppliedState)) { try { int n = 0; int percentage = 0; while (n < maxNumber && !TaskCanceled(asyncOp.UserSuppliedState)) { Thread.Sleep(100); //類比耗時操作 percentage = (int)((float)n / (float)maxNumber * 100); Work1ProgressChangedEventArgs progressChanageArgs = new Work1ProgressChangedEventArgs(maxNumber, percentage, asyncOp.UserSuppliedState); //任務1的進度通知 asyncOp.Post(new SendOrPostCallback(Work1ReportProgressCB), progressChanageArgs); n++; } } catch (Exception ex) { e = ex; } } this.Work1Complete(e, TaskCanceled(asyncOp.UserSuppliedState), asyncOp); } private void Work1Complete(Exception exception, bool canceled, AsyncOperation asyncOp) { if (!canceled) { lock (userStateToLifetime.SyncRoot) { userStateToLifetime.Remove(asyncOp.UserSuppliedState); } } Work1CompletedEventArgs e = new Work1CompletedEventArgs(exception, canceled, asyncOp.UserSuppliedState); //通知指定的任務已經完成 asyncOp.PostOperationCompleted(new SendOrPostCallback(Work1CompleteCB), e); //調用 PostOperationCompleted 方法來結束非同步作業的生存期。 //為某個特定任務調用此方法後,再調用其相應的 AsyncOperation 對象會引發異常。 } private void Work1ReportProgressCB(object state) { Work1ProgressChangedEventArgs e = state as Work1ProgressChangedEventArgs; OnWork1ProgressChanged(e); } private void Work1CompleteCB(object state) { Work1CompletedEventArgs e = state as Work1CompletedEventArgs; OnWork1Completed(e); } #region Work1的進度通知和任務完成的事件 public event Work1ProgressChangedEventHandler Work1ProgressChanged; protected virtual void OnWork1ProgressChanged(Work1ProgressChangedEventArgs e) { Work1ProgressChangedEventHandler temp = this.Work1ProgressChanged; if (temp != null) { temp(this, e); } } public event Work1CompletedEventHandler Work1Completed; protected virtual void OnWork1Completed(Work1CompletedEventArgs e) { Work1CompletedEventHandler temp = this.Work1Completed; if (temp != null) { temp(this, e); } } #endregion #endregion /// <summary> /// 取消指定userState的任務執行 /// </summary> /// <param name="userState"></param> public void CancelAsync(object userState) { AsyncOperation asyncOp = userStateToLifetime[userState] as AsyncOperation; if (asyncOp != null) { lock (userStateToLifetime.SyncRoot) { userStateToLifetime.Remove(userState); } } } /// <summary> /// 判斷指定userState的任務是否已經被結束。傳回值:true 已經結束; false 還沒有結束 /// </summary> /// <param name="userState"></param> /// <returns></returns> private bool TaskCanceled(object userState) { return (userStateToLifetime[userState] == null); } } public class Work1ProgressChangedEventArgs :ProgressChangedEventArgs { private int totalWork = 1; public Work1ProgressChangedEventArgs(int totalWork, int progressPercentage, object userState) : base(progressPercentage, userState) { this.totalWork = totalWork; } /// <summary> /// Work1的總工作量 /// </summary> public int TotalWork { get { return totalWork; } } } public class Work1CompletedEventArgs : AsyncCompletedEventArgs { public Work1CompletedEventArgs(Exception e, bool canceled, object state) : base(e, canceled, state) { } }}