在抄写书本上的定义之前,我先把我自己的理解先写出了,原因就是“回调聚集技巧”很重要,最好用。首先“回调聚集技巧”有一些不同于前面的两个技巧(等待直到结束聚集技巧和轮询聚集技巧),我们提交异步I/O请求,这里是一样的,但是,我们不需要再等待异步I/O请求的结果,因为,CLR将会为我们在线程池中新建一个线程,当异步I/O请求的结果回来时,线程池中新建的线程将会执行一个“特定”(我们自己定义)的一个方法。我这里说的不够详细,下面我来写一下书上的内容,写的很经典。
下面是方法回调聚集技巧的基本原理:
首先将异步I/O请求排队等候,然后线程继续执行它希望执行的任何事情。接着,当I/O请求完成时,Windows将工作项加入CLR的线程池的队列中。最后。线程池中的线程将工作项从队列中取出,并调用我们编写的一些方法(通过这种方式我们可以知道异步I/O操作已经完成)。现在,在回调方法内部,我们首先调用EndXX方法来获得异步操作的结果,然后就可以自由地继续处理结果。当回调方法返回时,线程池中的线程返回到线程池中准备服务下一个排队的工作项(或者等待下一个工作项的出现)。
理解方法回调聚集技巧的基本工作原理之后,下面来看看如何实现。这里再次给出FileStream的BeginRead方法的原型:
IAsyncResult BeginRead (Byte [ ] array , int32 offset , int32 numBytes , AsyncCallback userCallback , Object stateObject)
和BeginRead方法一样,每个BeginXX方法的最后两个参数都是相同的:一个是System.AsjyncCallback ,另一个是Object.AsyncCallback,它们是一个委托类型,它的定义如下所示:
Delegate void AsyncCallback ( IAsyncResult ar );
该委托表示必须实现的回调方法所需的签名。对于BeginXX方法的stateObject参数,可以传递我们希望的任何参数。该参数只是提供一种方式,即将操作排队的方法中的一些数据传递到处理操作完成的回调方法中。可以看出所见,回调方法将接收一个IAsyncResult对象的引用,而且回调方法可以通过查询IAsyncResult的AsyncState属性来获得状态对象的引用。下面的代码演示了方法回调聚集技巧:
例:
using System;
using System.IO;
using System.Threading;
namespace program
{
class wangjun
{
//将数组声明为Static ,以便Main方法和ReadIsDone方法可以访问它
private static byte[] s_data = new byte[100];
static void Main(string[] args)
{
//显示正在执行Main方法的线程的ID
Console.WriteLine("Main thread ID={0}",Thread.CurrentThread.ManagedThreadId);
//打开指示异步I/O操作的文件
FileStream fs = new FileStream(@"c:/a.txt", FileMode.Open, FileAccess.Read, FileShare.Read, 1024, FileOptions.Asynchronous);
//为FileStream对象初始化一个异步读操作,并将FileStream对象Fs传递给回调方法ReadIsDone
fs.BeginRead(s_data, 0, s_data.Length, ReadIsDone, fs);
//在这里执行一些其它代码将非常有用。。。。。
//出于演示目的,将主线程挂起
Console.ReadLine();
}
public static void ReadIsDone(IAsyncResult ar)
{
//显示正在执行ReadIsDone方法线程的ID
Console.WriteLine("ReadIsDone thread ID={0}",Thread.CurrentThread.ManagedThreadId);
//从IAsyncResult对象中提取FileStream对象(状态)
FileStream fs = (FileStream)ar.AsyncState;
//获取结果
Int32 bytesRead = fs.EndRead(ar);
//已经没有操作执行任务,关闭文件
fs.Close();
//现在可以访问字节数组并显示结果
Console.WriteLine("Number of bytes read={0}",bytesRead);
Console.WriteLine(BitConverter.ToString(s_data, 0, bytesRead));
}
}
}
第一,注意Main方法由ID号为1的主线程执行。而ReadIsDone方法由线程池中一个ID号为4的线程执行。这证明有两个不同的线程参与了这个程序的执行。第二,注意从BeginRead方法返回的IAsyncResult对象没有保存在Main方法的变量中。对此不做要求,因为CLR将把IAsyncResult对象传递到回调方法中。第三,在本例中我们将回调方法的名称作为第四个参数传递给BeginRead方法。第四,注意我们将Fs作为最后一个参数伟递给BeginRead方法(通过这种方式将FileStream对象传递回调方法)。回调方法通过查询被传递进来的IAsyncResult对象的AsyncState属性来获得FileStream对象的引用。