DotNet中异步编程(APM)的研究3
代理与异步编程 (by zguosir/gshzheng)

除了调用FCL中提供的异步方法外,.Net提供了一个非常有用的机制,使得任何一个方法都可以异步的调用,换言之,任何一个方法,都可以在单独的一个线程中异步的执行,执行完毕后将结果返回到调用线程。

这个机制就是使用代理(delegate)。代理是C#中引入的相当聪明的语言元素。从现象上看,代理象C++中的类型安全函数指针,所以常用于事件定义或其它回掉机制。本质上,代理是个特殊的类,它封装了函数调用的功能。尤其是代理的异步调用功能,使程序开发的灵活性大大增加,也大量地屏蔽了多线程开发的难度。

对任何方法,定义一个与其签名相同的代理,就可以通过代理的BeginInvoke()EndInvoke()来异步的调用。下面是一个演示了异步调用阶乘计算的例子。

Example5

public class UseDelegate

{

     //要调用的方法

     public static int Factorial(int n)

     {

         if(n<=1) return 1;

         return n*Factorial(n-1);

     }

     public delegate int DelegateFactorial(int n);

     //异步调用

     public static void AsyncCall(int n)

     {

         DelegateFactorial dele=new DelegateFactorial(Factorial);

         dele.BeginInvoke(n,new AsyncCallback(cb_AsyncCall),null);

     }

     private static void cb_AsyncCall(IAsyncResult ar)

     {

         DelegateFactorial d=((ar as AsyncResult).AsyncDelegate) as DelegateFactorial;

         int i=d.EndInvoke(ar);

         Console.WriteLine(i);

     }

}

 

代理的BeginInvoke()的返回值是个AsyncResult对象,它实现了IAsyncResult接口,并且也记录了调用者代理实例dele,可以在回调函数中将ar强行转化为AsyncResult并取得AsyncDelegate后,通过它来调用EndInvoke()。注,其它的异步调用BeginOperation返回的对象不一定是AsyncResult类型,也未必有AsyncDelegate属性,需要借助于AsyncState参数等方式传入。

 

代理本质上是个类,继承自MulticastDelegateMulticastDelegate 是一个特殊类。编译器和其他工具可以从此类派生,但是开发人员不能显式地从此类进行派生。 当定义了一个代理,

public delegate int DelegateFactorial(int n);

编译器将会自动生成一个等价的类

public class DelegateFactorial : System.MulticastDelegate

{

     public DelegateFactorial(Object target, Int32 methodPtr);

     public  virtual void Invoke(Int32 n);

     public virtual IAsyncResult BeginInvoke(Int32 n,AsyncCallback callback, Object state);

     public virtual int EndInvoke(IAsyncResult result);

}

其中,构造方法在实例化一个代理对象时由CLR调用,并自动传入当前对象到第一个参数(如果是静态方法则传入null);Invoke()方法就是直接调用时的同步方法,目前的vs.net编译器不允许直接调用此方法;BeginInvoke()EndInvoke()方法就是一对异步方法,它们的方法签名的格式与代理定义相一致:

l           BeginInvoke()的后面固定为指定回调函数的两个参数;

l           代理定义的所有参数(包括ref,out参数),将作为BeginInvoke前面的参数;

l           EndInvoke()的最后一个参数,固定为IAsyncResult

l           如果代理的参数列表中有ref,out参数,将作为EndInvoke前面的参数;

l           EndInvoke()的返回值即为代理的返回值。