异步操作典型应用于要完成一项耗时或者涉及I/O的任务时,如,复杂计算,打开大型文件,连接远程计算机,进行数据库查询等等。异步操作在与程序主线程不同的线程中执行,当应用程序异步地执行操作时,主线程并不被挂起,而是可以继续的向下执行。
在.Net类库中(Framework Class Library,FCL),有许多类支持异步操作,如下表,
Area |
Class Name |
Method Name |
I/O |
Sysetm.IO.Stream |
BeginRead() BeginWrite() |
Dns |
System.Net.Dns |
BeginrResolve() BeginGetHostAddress() |
Socket |
System.Net.Sockets .Socket |
BeginAccept() BeginReceive() BeginSend() |
Web |
System.Net.WebRequest |
BeginGetRequestStream() BeginGetResponse() |
Sql |
System..Data.SqlClient .SqlCommand |
BeginExecuteNonQuery() BeginExecuteReader() |
Delegate |
System.MulticastDelegate |
BeginInvoke() |
异步操作要提供两个对应的方法名BeginOperation()和EndOperation(),通过这两个方法来分别来开启和结束一个异步操作Operation。如FileStream类提供BeginRead()和EndRead()方法,异步地从文件种读取一定的字节数。一般的,异步方法都会有与之对应的同步操作,如Read()为文件读取的同步方法。
当调用异步方法BeginOperation(),应用程序可以继续在调用线程(主线程)上执行接下来的指令,而异步操作将在另外的线程上执行。对每一次的调用BeginOperation(),程序需要在某处相应的调用EndOperation()以得到操作的返回结果。
调用异步方法BeginOperation()后,该方法返回一个实现了IAsyncResult接口的对象。这个对象保存了关于此异步操作执行的一些信息,它可以代表这次异步操作。IAsyncResult对象就像.Net CLR开给应用程序的收据,表示接收了此次异步操作的请求,并尝试开始执行。异步操作可能很快完成,可能需一段时间后完成,也可能永远也完成不了。
IAsyncResult是APM编程的核心,它提供四个只读属性,在异步操作启动后,应用程序可以查询这些属性,以确定操作是否完成。下表显示了IAsyncResult接口的属性,
Property |
Description |
AsyncState |
获取开启异步操作时传入的状态对象,一般用于方法回调方式 |
IsCompleted |
获取操作是否完成的标志,一般用于轮巡方式 |
AsyncWaitHandler |
获取用于等待操作完成的WaitHandle,一般用于等待直到完成方式 |
CompletedSynchronously |
获取异步操作是否同步完成的指示。 |
异步方法BeginOpertion()方法拥有与对应的同步方法一致的参数(无论是传值还是传引用),另外,所有的BeginOpertion()方法参数列表最后都会包含两个附加的参数,第一个为AsyncCallBack代理类型对象,当异步操作完毕后将自动调用由它代表的方法;第二个附加的参数为用户自定义的状态对象,它将存储在返回的IAsyncResult对象的AsyncState属性中。
如果操作结束后不需要调用某个回调函数,或者不需要传入一个自定义的状态对象,在实际函数调用中,这两个最后的参数都可以为null。
调用EndOperation()关闭异步操作执行。此时必须传入由BeginOperaton()返回的IAsyncResult对象,才正式关闭此异步操作,以及释放和回收异步操作所用的资源。EndOperation()的返回值相当于直接调用同步方法的返回值,如EndRead()方法返回从FileStream中实际读到的字节数。
如果在一个由IAsyncResult表示的异步操作还没有完成之前,调用EndOperation(),则将挂起当前线程,直到异步操作完成。异步操作中引起的异常,将由EndOperation()方法抛出来。对同一个IAsyncResult异步操作,调用多次EndOperation()方法,将引起不可预知的后果。同样,调用EndOperation(),传入的不是由同一个异步操作BeginOperation()返回的IAsyncResult对象,其后果也不可预知。