异步编程模式(二):基于委托的异步编程模式
上一节日记中,项目所展示的异步编程模式到底是怎么实现的?
上一节代码中定义了一个委托:
public delegate long CalculateFolderSizeDelegate(string FolderName);
经过编译器,在编译上述委托语句时,会自动产生以下的类模板:
public sealed class CalculateFolderSizeDelegate: System.MulticastDelegate
{
public CalculateFolderSizeDelegate(Object target , int methodPtr){.......}
public void virtual Invoke(string FolderName){........}
public virtual IAsyncResult BeginInvoke(
string FolderName , AsyncCallback callback , Object asyncState){...........}
publlic virtual void EndInvoke(IAsyncResult result){.........}
}
当使用普通的同步模式直接通过委托变量调用方法时:
CalculateFolderSizeDelegate d = CalculateFolderSize;
long size = d("C:\\Windows");
编译器会将这一调用转换为一个对应的Invoke方法调用。这是一个同步调用,与调用者代码执行在峿 个线程中。
要想实现异步调用,就不能 使用 Invoke方法,而必须使用 BeginInvoke 和 EndInvoke 方法。
一、关于BeginInvoke
编译器为委托数据类型生成的BeginInvoke 方法是这个“模样”:
public IAsyncResult BeginInvoke(<输入和输出变量>, AsyncCallback callback,object asyncState)
参数的第一部分是定义委托时确定的方法签名中的参数列表,第二个参数callback是当异步调用结束时自动回调的方法,第三个参数asyncState用于向第二个
参数所确定的callback回调方法提供额外的信息,比如如果要回调的方法有参数,则asyncState可用于“填充”这些参数值。
这几个参数将在后面的日记中详细说明用法
需要注意的是:BeginInvoke方法返回一个IAsyncResult接口的对象。
此接口定义如下:
public interface IAsyncResult
{
object AsyncState {get;}
WaitHandle AsyncWaitHandle{get;}
bool CompletedSynchronously {get;]
bool IsCompleted{get;}
}
请注意,BeginInvoke方法中的第三个参数被原封不动地包装成了IAsyncresult接口中的属性 AsyncState.
另一个有趣的字段是 IsCompleted,启动异步调用 的线程可以通过这个字段查询异步调用是否完成。
bool类型的属性 CompletedSynchronously指明 BeginInvoke调用是否同步完成。
另一个成员AsyncWaitHandle属性是一个 "WaitHandle(等待句柄)"对象,当异步操作完成时,此句柄居于 signaled状态。
二、关于EndInvoke
编译器为委托数据类型生成的 EndInvoke方法是这个模样:
public <方法返回值类型> EndInvoke( <声明为ref或out参数> , IAsyncResult result)
前面介绍过,BeginInvoke方法负责生成一个 IAsyncResult 接口类型的对象并填充其字段。
借助于 IAsyncResult 对象,EndInvoke 方法不断查询异步调用的方法是否执行完毕,根据
委托类型的定义,EndInvoke方法知道被异步调用的方法所有的参数,因此当 EndInvoke方法发现异步调用完成时,
它取出异步调用方法执行的结果作为其返回值,如果异步调用方法有声明为ref和out的参数,它也负责填充它。
这样,启动异步调用的调用者线程就可以获取异步调用方法的结果了。
正由于EndInvoke 方法有一个不断轮询的过程,所以主线程程序在执行到调用 EndInvoke方法时全暂停,等待异步调用方法完成,取回
结果后再继续运行。