DotNet中异步编程(APM)的研究2
三种聚集方式rendevousing (by zguosir/gshzheng)
这个名字挺拗口,其实就是如何知道异步操作结束

异步操作结束后,CLR将自动地

l           设置IAsyncResult对象的IsCompleteTrue

l           IAsyncResultAsyncWaitHandled对象设上信号;

l           如果设置了回调函数,则调用它。

 

程序有三种方式可以获知异步操作是否已经执行完成,我们称之为聚集技巧(Rendezvous Techniques)。等待直到完成(wait-until-done)、轮询(polling)和回调方法(callback)。选择哪一个依赖于程序是否有当异步操作执行完毕后要执行的指令。(实际中,最常用的是回调方法)

 

 

1.等待直到完成

调用线程启动一个异步操作后将被挂起,直到异步操作结束。在这种方式下,所谓的异步操作从执行效果上看就相当于其对应的同步操作了。有两种方式可以挂起当前调用线程,等待异步操作的完成。

l           直接调用EndOperation()方法,等待执行结果;

l           使用IAsyncResult对象AsnycWaitHandle.WaitOne()方法来等待。

下面的例子演示了用第一种方式通过异步操作从读取文件内容:

Example1

public class WaitUntilDone0

{

     public static void ReadFileAsync()

     {

         //打开文件并指定异步操作

         FileStream fs=new FileStream(@"c:\apm.txt",FileMode.Open,

FileAccess.Read,FileShare.Read,1024,true);

         //开启异步读取

         byte[] data = new byte[100];

         IAsyncResult ar = fs.BeginRead(data,0,data.Length,null,null);

         //执行某些代码...

         try

         {

              //等待直到异步读取完成,并结束异步操作

              int cnt = fs.EndRead(ar);

              fs.Close();

              Console.WriteLine(BitConverter.ToString(data,0,cnt));

         }

         catch(Exception e)

         {

              Console.WriteLine(e.Message);

         }

     }

}

 

下面的例子演示了第二种方式,利用异步操作从读取文件内容:

Example2

//开启异步读取

byte[] data = new byte[100];

IAsyncResult ar = fs.BeginRead(data,0,data.Length,null,null);

//执行某些代码...

//等待直到异步读取完成

ar.AsyncWaitHandle.WaitOne();

try

{

     //结束异步操作,并得到结果

     int cnt = fs.EndRead(ar);

     fs.Close();

}

 

 

上面两个程序并没有有效的利用APM,因为在BeginRead()之后,我们就立即调用了EndRead()(或WaitOne())方法,调用线程挂起进入了休眠状态,等待操作完成。在实际应用中,如果在BeginReadEndRead之间放入一些代码,或者对这两个函数的调用分配在不用的方法调用中,就会体现APM的价值了。

 

这两种方法的区别就是Example2中将等待与结束异步操作分离开了

 

 

2.轮询polling

使用轮询技巧,程序在开启异步操作后不需要等待其执行完毕,而可以干其它的事情。当在某次检查IAsyncResultIsComplete属性为true时,即可知道操作已完成,调用EndOperation()得到其结果。Example3是用轮询技巧来完成异步读文件的功能:

Example3

//开启异步读取

byte[] data = new byte[100];

IAsyncResult ar = fs.BeginRead(data,0,data.Length,null,null);

//执行某些代码...

//不停查询异步操作执行的状态,直到执行完毕

while(!ar.IsCompleted)

{

     //刷新用户界面

     Console.Write(".");

     Thread.Sleep(10);

}

Console.WriteLine();

try

{

     //结束异步操作,并得到结果

     int cnt = fs.EndRead(ar);

     fs.Close();

}

 

本例中,为了简单的目的,直接在调用了BeginRead()后,就开始循环查询了,实际编写使用轮询技巧的代码时,一般是让单独的线程定期地来查询异步操作是否结束。

 

3.回调callback

在构建高性能和可扩展的应用程序架构时,回调方法是最好用的聚集技巧。它在任何时候都不用将调用线程挂起等待,也不会需要另外的不定期的检查操作是否完成。

 

回调方法是一个符合AsyncCallBack签名的方法,在调用开启异步操作时,将回调方法传入BeginOperation()。当异步操作执行完毕后,将自动调用这个方法,在这个方法中调用EndOperation()得到执行的结果。

Example4

public class Callback

{

     //class-scope定义缓冲区

     private static byte[] data=null;

     public static void cb_read(IAsyncResult ar)

     {

         FileStream fs= ar.AsyncState as FileStream;

         try

         {

              //结束异步操作,并得到结果

              int cnt = fs.EndRead(ar);

              fs.Close();

              Console.WriteLine(BitConverter.ToString(data,0,cnt));

         }

         catch(Exception e)

         {

              Console.WriteLine("An exception occurred while processing :{0}",e.Message);

         }

     }

 

     public static void ReadFileAsync()

     {

         //打开文件并指定异步操作

         FileStream fs=new FileStream(@"c:\apm.txt",FileMode.Open,

FileAccess.Read,FileShare.Read,1024,true);

 

         //开启异步读取

         data = new byte[100];

         IAsyncResult ar = fs.BeginRead(data,0,data.Length,new AsyncCallback(cb_read),fs);

         //执行某些代码...

         Console.ReadLine();

     }

}