委托异步调用时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 添加类型安全的委托
}

各位同学可以根据自己的需要添加类型安全的实现。

时间: 2024-11-07 15:32:23

委托异步调用时BeginInvoke的陷阱处理的相关文章

C#如何使用异步编程【BeginInvoke/EndInvoke】

怎么使用异步,就是用委托进行处理,如果委托对象在调用列表中只有一个方法,它就可以异步执行这个方法.委托类有两个方法,叫做BeginInvoke和EndInvoke,它们是用来异步执行使用. 异步有三种模式 等待模式,在发起了异步方法以及做了一些其它处理之后,原始线程就中断,并且等待异步方法完成之后再继续. 轮询模式,原始线程定期检查发起的线程是否完成,如果没有则可以继续做一些其它的事情. 回调模式,原始线程一直在执行,无需等待或检查发起的线程是否完成.在发起的线程中的引用方法完成之后,发起的线程

异步和多线程,委托异步调用,Thread,ThreadPool,Task,Parallel,CancellationTokenSource

1 进程-线程-多线程,同步和异步2 异步使用和回调3 异步参数4 异步等待5 异步返回值 5 多线程的特点:不卡主线程.速度快.无序性7 thread:线程等待,回调,前台线程/后台线程, 8 threadpool:线程池使用,设置线程池,ManualResetEvent9 Task初步接触 10 task:waitall waitany continueWhenAny continueWhenAll  11并行运算Parallel 12 异常处理.线程取消.多线程的临时变量和lock13 A

【转】堆栈跟踪中收到一个UnhandledExceptionFilter调用时,如何查找问题异常堆栈

定义没有异常处理程序来处理引发的异常时调用UnhandledExceptionFilter函数.函数通常将异常传递到捕获并处理它所尝试的 Ntdll.dll 文件. 在某些情况下,在其中存在的进程内存快照,您可以看到一个线程持有的锁点的线程调用UnhandledExceptionFilter函数.在这些情况下,您可以按照本文来标识导致异常的 DLL 中的步骤. 通过使用 Windbg.exe 打开转储文件 下载并安装调试程序.若要下载调试程序,请访问下面的 Microsoft 网站:Micros

C#委托异步调用

废话不多说,直接上代码(PS:我就喜欢简单.直接.粗暴) using System;using System.Collections.Generic;using System.Linq;using System.Runtime.Remoting.Messaging;using System.Text;using System.Threading;using System.Threading.Tasks; namespace 异步调用委托{    class Program    {       

委托-异步调用-泛型委托-匿名方法-Lambda表达式-事件

1. 委托 From: http://www.cnblogs.com/daxnet/archive/2008/11/08/1687014.html 类是对象的抽象,而委托则可以看成是函数的抽象.一个委托代表了具有相同参数列表和返回值的所有函数. [csharp] view plaincopy class Program { delegate int CalculateDelegate(int a, int b); int add(int a, int b) { return a + b; } s

委托异步返回值

1:如何新起线程 新起一个线程的方法,可以使用Thread,BackgroundWorker ,ThreadPool,控件.BeginInvoke,委托.BeginInvoke,Timer. 2:异步调用返回值 上码: using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Threading; using System.

C# 委托异步 和 async /await 两种实现的异步

最近频繁使用异步所以自己综合的学习了一把异步相关的知识,自己稍加整理了一下(这也是我试着写的第一篇,如果有不对的,希望大神来指正!) 首先是 委托实现的异步 class Program { public delegate int weituo();//定义了个委托 public int xxx() { Thread.Sleep(3000); Console.WriteLine("11111."); return 1; } ///定义了个方法 static void Main(strin

ajax循环调用时,只返回最后一次的值,后台执行最后一次数据

1. 代码:var i;for(i=0;i<10;i++){      ajaxServise(i);} 在for循环中调用ajax方法  补充页面上的数据,这样写是错误的,他不会每执行一次for循环就执行一次ajax方法,而是等for循环结束才去执行ajax方法,所以导致ajax只被执行一次.然而当修改如下: for(i=0;i<10;i++){      alert("aa");      ajaxServise(i);}与上面不同的是在每次调用的时候都弹出一个窗体 

cocos2d-x学习笔记(四)粒子系统使用中多次调用时GL calls不断增加的解决办法

今天在编写一个射击类的游戏时用到粒子系统,点击屏幕发射***打中敌机后产生爆炸的粒子效果,以为快要完工的时候,发现每次产生爆炸效果GL calls(每一帧中openGL指令的调用次数)就增加一次,在手机运行肯定出问题,经过仔细分析发现确实是粒子系统没有被移除的原因. 开始的程序如下: //根据 plist 文件创建粒子系统 ParticleSystem* m_emitter1 = ParticleSystemQuad::create("boom.plist"); m_emitter1-