委托異步調用時BeginInvoke的陷阱處理


這個陷阱來自於一個需求:需要異步在后台處理數據,處理完后觸發處理完成的事件,大概是這么寫的:

EmployeeCollection data = new EmployeeCollection();
data.Loaded += data_Loaded;
Action<EmployeeCollection> action = (d) => {
    DalHelper.Fill(data);
    data.RaiseEventLoaded();
};
action.BeginInvoke(data, null, null);

挺簡單的代碼,陷阱也在其中。假如DalHelper.Fill(data)拋出了一個異常,那么對data.RaiseEventLoaded()就不會執行,依賴於data.Loaded事件的代碼也不會執行,這是一個bug,應該在委托執行中加入一個try...catch語句,或者在某個地方調用委托的EndInvoke方法,來處理執行中可能的異常。

 

為了這么一個簡單的需求,加入try...catch或者調用委托的EndInvoke都太復雜了,僅僅只想滿足假如執行失敗,就把異常拋出來,即使將當前進程結束也沒事。本着一次編寫,多次使用的原則,專門設計了一個幫助類來專職這類委托的異步調用。幫助類的代碼如下:

public class EventHelper {
   public static void UnsafeBeginInvoke(Delegate del,params object[] args){
      AsyncFire asyncFire = InvokeDelegate;
      asyncFire.BeginInvoke(del, args, ThrowCallback, asyncFire);
   }     

    delegate void AsyncFire(Delegate del,object[] args);

    static void InvokeDelegate(Delegate del,object[] args){
        del.DynamicInvoke(args);
    }

   static void ThrowCallback(IAsyncResult ar) { 
       AsyncFire asyncFire = ar.AsyncState as AsyncFire;
       asyncFire.EndInvoke(ar);
   }
}

核心實現是將委托的調用封裝起來,在另外一個委托中去調用,然后對另外的那個委托用EndInvoke來釋放可能的異常,這樣就能夠發現單純的調用BeginInvoke后委托執行時引發的異常。這樣修改后,剛才的代碼就可以這樣來調用:

EmployeeCollection data = new EmployeeCollection();
data.Loaded += data_Loaded;
Action<EmployeeCollection> action = (d) => {
    DalHelper.Fill(data);
    data.RaiseEventLoaded();
};
EventHelper.UnsafeBeginInvoke(action, data);

代碼還如最初的設計那么簡單,而且真要是委托中發生了異常,也能夠發現這個錯誤,而不是讓這個錯誤被掩蓋。

 

另外,剛才的實現不是類型安全的,類型安全可以通過重載來解決,例子如下:

public class EventHelper {
   public static void UnsafeBeginInvoke(Delegate del,params object[] args){
      AsyncFire asyncFire = InvokeDelegate;
      asyncFire.BeginInvoke(del, args, ThrowCallback, asyncFire);
   }     

    delegate void AsyncFire(Delegate del,object[] args);

    static void InvokeDelegate(Delegate del,object[] args){
        del.DynamicInvoke(args);
    }

   static void ThrowCallback(IAsyncResult ar) { 
       AsyncFire asyncFire = ar.AsyncState as AsyncFire;
       asyncFire.EndInvoke(ar);
   }

   #region 添加類型安全的委托

   public static void BeginInvoke(Action del){
      UnsafeBeginInvoke(del);
   }

   public static void BeginInvoke<T,U>(Action<T,U> del,T t, U u){
      UnsafeBeginInvoke(del,t,u);
   }

   public static void BeginInvoke<T,U,V>(Action<T,U> del,T t, U u, V v){
      UnsafeBeginInvoke(del,t,u,v);
   }

   #endregion 添加類型安全的委托
}
View Code

各位同學可以根據自己的需要添加類型安全的實現。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM