每个委托都有三个方法:Invoke、BeginInvoke、EndInvoke。第一个方法是委托指定函数的同步调用,另外两个是异步调用。
BeginInvoke方法,调用后立即返回,不等待调用结果。EndInvoke方法,用于检索调用结果。调用BeginInvoke后可随时调用 EndInvoke 方法;如果异步调用未完成,EndInvoke 将一直阻塞到异步调用完成。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数(在 Visual Basic 中为 <Out> ByRef 和 ByRef)以及由 BeginInvoke 返回的 IAsyncResult
BeginInvoke 异步方法签名的规则是:
- 包括所有 IN 参数。
- 包括所有 OUT 参数。
- 包括所有 IN/OUT 参数。
- 包括所有 ByRef 参数。
- 将 AsyncCallback 和 AsyncState(可通过 IAsyncResult 接口的 AsyncState 属性获得)作为最后两个参数。
- 返回 IAsyncResult。
EndInvoke 异步方法签名的规则是:
- 包括所有 IN/OUT 参数。
- 包括所有 OUT 参数。
- 包括所有 ByRef 参数。
- 将 IAsyncResult 作为最后一个参数。
- 从原始方法签名返回原始返回类型。
结果对象 (IAsyncResult) 是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。结果对象被传递到结束操作,该操作返回调用的最终返回值。在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。
AsyncCallback 委托
AsyncCallback 委托用于指定在开始操作完成后应被调用的方法。下面是该委托的签名,AsyncCallback 委托被作为开始操作上的第二个到最后一个参数传递:
public delegate void AsyncCallback(IAsyncResult ar);
IAsyncResult 接口
IAsyncResult 接口用于监视和管理异步操作。该接口是从开始操作返回的并被传递到结束操作,以将开始操作和结束操作相关联。如果回调被指定为开始操作的一部分,则 AsyncResult 被传递到回调。以下代码阐释有关 IAsyncResult 接口的属性,该接口可用于监视异步操作的状态并获取还可被传递到开始操作中的异步状态对象:
public interface IAsyncResult
{
Object AsyncState { get; } //该属性为BeginInvoke参数中的最后一个参数对象
WaitHandle AsyncWaitHandle { get; }
bool CompletedSynchronously { get; }
bool IsCompleted { get; } //该属性判断异步调用是否结束
}
- AsyncState
返回在开始操作方法调用中作为最后一个参数提供的对象。
- AsyncWaitHandle
AsyncWaitHandle 属性返回 WaitHandle,后者可用于执行 WaitHandle.WaitOne、WaitAny 或 WaitAll。
注意 直到 AsyncWaitHandle 属性被读取时,实现 IAsyncResult 的对象才需要创建 WaitHandle;执行时间由实施者决定。如果实施者创建了 WaitHandle,则实施者需要负责在适当的时候发出 WaitHandle 信号终止等待。例如,当异步调用的方法返回时,AsyncResult 将代表调用方终止等待。创建后,WaitHandle 应保持活动状态,直到用户调用结束异步操作的方法。此时,可以丢弃 AsyncWaitHandle 后的对象。
- CompletedSynchronously
如果开始操作调用已同步完成,则 CompletedSynchronously 属性将被设置为 true。
- IsCompleted
在服务器已处理完调用后,IsCompleted 属性将被设置为 true。
调用了 BeginInvoke 后,可以:
- 进行某些操作,然后调用 EndInvoke 一直阻塞到调用完成。
- 使用 IAsyncResult.AsyncWaitHandle 获取 WaitHandle,使用它的 WaitOne 方法将执行一直阻塞到发出 WaitHandle 信号,然后调用 EndInvoke。
- 轮询由 BeginInvoke 返回的 IAsyncResult,确定异步调用何时完成,然后调用 EndInvoke。
- 将用于回调方法的委托传递给 BeginInvoke。该方法在异步调用完成后在 ThreadPool 线程上执行,它可以调用 EndInvoke。
注: 始终在异步调用完成后调用 EndInvoke。
以下是关于异步委托的测试代码:
using System;
using System.Threading;
public class AsyncDemo {
// The method to be executed asynchronously.
//
public string TestMethod(int callDuration, out int threadId) {
Console.WriteLine("Test method begins.");
Thread.Sleep(callDuration);
threadId = AppDomain.GetCurrentThreadId();
return "MyCallTime was " + callDuration.ToString();
}
}
// The delegate must have the same signature as the method
// you want to call asynchronously.
public delegate string AsyncDelegate(int callDuration, out int threadId);
public class AsyncMain {
static void Main(string[] args) {
// The asynchronous method puts the thread id here.
int threadId;
// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();
// Create the delegate.
AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
// Initiate the asychronous call.
IAsyncResult ar = dlgt.BeginInvoke(3000,
out threadId, null, null);
Thread.Sleep(0);
Console.WriteLine("Main thread {0} does some work.",
AppDomain.GetCurrentThreadId());
// Wait for the WaitHandle to become signaled.
ar.AsyncWaitHandle.WaitOne();
/*
这个是轮询异步执行状态
// Poll while simulating work.
while(ar.IsCompleted == false)
{
Thread.Sleep(10);
}
*/
// Call EndInvoke to Wait for the asynchronous call to complete,
// and to retrieve the results.
string ret = dlgt.EndInvoke(out threadId, ar);
Console.WriteLine("The call executed on thread {0}, with return value /"{1}/".", threadId, ret);
}
}
以下代码是用回调函数执行EndInvoke方法
public class AsyncMain {
// Asynchronous method puts the thread id here.
private static int threadId;
static void Main(string[] args) {
// Create an instance of the test class.
AsyncDemo ad = new AsyncDemo();
// Create the delegate.
AsyncDelegate dlgt = new AsyncDelegate(ad.TestMethod);
// Initiate the asychronous call. Include an AsyncCallback
// delegate representing the callback method, and the data
// needed to call EndInvoke.
IAsyncResult ar = dlgt.BeginInvoke(3000,
out threadId,
new AsyncCallback(CallbackMethod),
dlgt );
Console.WriteLine("Press Enter to close application.");
Console.ReadLine();
}
// Callback method must have the same signature as the
// AsyncCallback delegate.
static void CallbackMethod(IAsyncResult ar) {
// Retrieve the delegate.
AsyncDelegate dlgt = (AsyncDelegate) ar.AsyncState;
// Call EndInvoke to retrieve the results.
string ret = dlgt.EndInvoke(out threadId, ar);
Console.WriteLine("The call executed on thread {0}, with return value /"{1}/".", threadId, ret);
}
}