当我们的程序顺序执行的时候,如果我们调用了一个很耗时的方法,那么应用程序会使当前的线程处于阻塞状态,直到方法的调用完成再继续执行。这样的方法调用被认为是同步的。如果这个方法的调用可能花费的时间会更长,而我们不想立即取得方法所返回的结果,或者我们根本就不让方法返回结果的话,这种等待是没有必要的。那我们就可以利用异步委托来把我们的方法做成异步的调用。
异步调用并不是要减少线程的开销, 它的主要目的是让调用方法的主线程不需要同步等待在这个函数调用上, 从而可以让主线程继续执行它下面的代码.与此同时, 系统会通过从ThreadPool中取一个线程来执行,帮助我们将我们要写/读数据.由于不需要我们等待, 我们等于同时做了两件事情. 这个效果跟自己另外启动一个线程来执行等待方式的写操作是一样的.
我们先来了解这2个方法和一个委托和一个接口:
(1)
BeginInvoke方法用于启动异步调用
它与您需要异步执行的方法具有相同的参数,只不过还有两个额外的参数,将 AsyncCallback 和 AsyncState(可通过 IAsyncResult 接口的
AsyncState 属性获得)作为最后两个参数,如没有可以为空.
BeginInvoke立即返回,不等待异步调用完成。
BeginInvoke返回IasyncResult,可用于监视调用进度。
结果对象IAsyncResult是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。
结果对象被传递到结束操作,该操作返回调用的最终返回值。
在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。
(2)
EndInvoke方法用于检索异步调用结果。
在调用BeginInvoke后可随时调用EndInvoke方法,注意:始终在异步调用完成后调用EndInvoke.
如果异步调用未完成,EndInvoke将一直阻塞到异步调用完成。
EndInvoke的参数包括需要异步执行的方法的out和ref参数以及由BeginInvoke返回的IAsyncResult。
要注意的是,始终在异步调用完成后调用EndInvoke
(3)
AsyncCallback委托用于指定在开始操作完成后应被调用的方法
AsyncCallback委托被作为开始操作上的第二个到最后一个参数传递
代码原型如下:
[Serializable]
public delegate void AsyncCallback(IAsyncResult ar);
(4)
IAsyncResult接口
它表示异步操作的状态.
该接口定义了4个公用属性
实际上,发起和完成.NET异步调用有4种方案可供你选择
1.方案1-自己调用EndInvoke方法
异步执行方法的最简单方式是以BeginInvoke开始,对主线程执行一些操作,然后调用EndInvoke,EndInvoke直到异步调用完成后才返回
还是先来段自己喜欢的控制台代码:
结果如下:
前者的时间刻度大大小于后者
我们可以明显地看到异步运行的速度优越性
2.方案2-采用查询(IsCompleted属性)
IAsyncResult.IsCompleted属性获取异步操作是否已完成的指示,发现异步调用何时完成.
再次修改代码这几行代码:
AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
IAsyncResult ia=asy.BeginInvoke(null,null);
c.Event2();
while(!ia.IsCompleted)
{
}
asy.EndInvoke(ia);
3.方案3-采用AsyncWaitHandle来等待方法调用的完成
IAsyncResult.AsyncWaitHandle属性获取用于等待异步操作完成的WaitHandle
WaitHandle.WaitOne方法阻塞当前线程,直到当前的WaitHandle收到信号
使用WaitHandle,则在异步调用完成之后,但在通过调用EndInvoke结果之前,可以执行其他处理
再次修改代码这几行代码:
AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
IAsyncResult ia=asy.BeginInvoke(null,null);
c.Event2();
ia.AsyncWaitHandle.WaitOne();
4.方案4-利用回调函数
如果启动异步调用的线程不需要处理调用结果,则可以在调用完成时执行回调方法
要使用回调方法,必须将代表该方法的AsyncCallback委托传递给BeginInvoke
再次修改代码这几行代码:
AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
asy.BeginInvoke(new AsyncCallback(c.CallbackMethod),asy);
c.Event2();