委托对象的调用列表中只有一个方法(我们通常称之为引用方法)时,可以进行异步执行。委托类有两个方法:BeginInvoke和EndInvoke就是用来实现这一效果的。
通常我们在实际使用过程中,这种异步执行的方法常常采用三种模式:
首先我们需要了解BeginInvoke的一些重要事项:
delegate long MyDel(int first, int second);
...
static long Sum (int x,int y){...}
...
MyDel del=new MyDel(Sum);
IAsyncResult iar = del.BeginInvoke(3,5,null,null);
EndInvoke 用来获取异步方法调用返回的值,并且清除释放线程中使用的资源。特征如下:
如下代码示例了一个EndInvoke的调用,它必须以IAsyncResult对象的引用为参数:
long result = del.EndInvoke(iar);
EndInvoke提供了从异步方法返回的所有输出,包括ref和out参数。如果委托的引用方法有ref和out参数,它们就必须包含在EndInvoke的参数列表中。例如:
long result = del.EndInvoke(out int someInt, iar);
在这种模式下,原始线程发起一个异步方法的调用,做一些事情处理,然后停止并等待,直到开启线程结束。
using System;
using System.Threading;
delegate long MyDel (int first, int second);
class Program
{
static long Sum(int x, int y)
{
Console.WriteLine(“ Inside Sum”);
Thread.Sleep(100);
return x+y;
}
static void Main()
{
MyDel del=new MyDel(Sum);
Console.WriteLine(“Before BeginInvoke”);
IAsyncResult iar=del.BeginInvoke(3, 5, null, null);
Console.WriteLine(“ After BeginInvoke”);
Console.WriteLine(“Doing stuff”);
long result = del.EndInvoke(iar);
Console.WriteLine(“After EndInvoke:{0}”, result);
}
}
AsyncResult 是方法的必要部分,BeginInvoke 返回一个IAsyncResult接口的引用(内部是AsyncResult类的对象)。AsyncResult类表现了异步方法的状态。
在轮询模式中,原始线程发起了异步方法的调用,做一些其他处理,然后使用IAsyncResult对象的IsCompleted属性定期检查开启的线程是否完成。如果完成则继续向下执行,否则继续做它的其他处理,直到下一次检查。
delegate long MyDel(int x, int y);
class program
{
long Sum(int x, int y)
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(100);
return x + y;
}
static void Main()
{
MyDel del = new MyDel(Sum);
IAsyncResult iar = del.BeginInvoke(3, 5, null, null);
Console.WriteLine("After BeginInvoke");
while (!iar.IsCompleted)
{
Console.WriteLine("Not Done");
for (long i = 0; i < 10000000; i++) ;
}
Console.WriteLine("Done");
long result = del.EndInvoke(iar);
Console.WriteLine("Result:{0}", result);
}
}
在之前的等待结束模式和轮询模式中,初始线程都是继续自己的控制流程,直到它知道开启的线程已经完成,然后获取结果并继续。
回调模式则与之不同,一旦初始线程发起了异步方法,由它自己管理,就不再进行管理。异步方法调用结束后,系统自动调用一个用户自定义的方法来自行处理结果,并且调用委托的EndInvoke方法。这个用户自定义的方法做做回调方法或简称回调。
BeginInvoke的参数列表中最后两个额外参数就是供回调方法使用的。
1)回调方法
回调方法的签名和返回类型必须与AsyncCallback委托类型所描述的形式一致。它需要方法接受一个IAsyncResult作为参数并且返回类型是void,如下所示
void AsyncCallback(IAsysnResult iar)
我们有多种方式可以为BeginInvoke方法提供回调方法。由于BeginInvoke中的callback参数是AsyncCallback类型的委托,我们可以以委托形式提供,或者,我们也可以只提供回调方法的名称,让编译器为我们创建委托,两种方法是完全等价的。
IAsyncResult iar1=del.BeginInvoke(3,5,new AsyncCallback(CallWhenDone),null)
IAsyncResult iar2=del.BeginInvoke(3,5,CallWhenDone,null)
BeginInvoke的另外一个参数是发送给回调方法的对象。它可以是任何类型的对象,但是参数类型是object。所以在回调方法中,我们必须转换成正确的类型。
2)在回调方法中调用EndInvoke
在回调方法内,我们的代码应该调用委托的EndInvoke方法来处理异步方法执行后的输出值。要调用委托的EndInvoke方法,我们肯定需要委托对象的引用,而它在初始线程中,不在开启的线程中。如果不使用state参数作为其他用途,可以使用它发送委托的引用给回调方法。
IAsyncResult iar = del,BeginInvoke(3,5,CallWhenDone,del);
using System.Runtime.Remoting.Messaging; //包含AsyncResult类
void CallWhenDone(IAsyncResult iar)
{
AsyncResult ar = (AsyncResult)iar; // 获取类对象的引用
MyDel del = (MyDel)ar.AsyncDelegate; // 获取委托的引用
long sum = del.EndInvoke(iar); // 调用EndInvoke方法
...
}
如此我们就完成了一个使用回调模式的完整示例。为了方便阅读,我们把所有知识点放在一起:
using System;
using System.Threading;
using System.Runtime.Remoting.Messaging; //包含AsyncResult类
namespace YbydjyLibrary
{
delegate long MyDel(int first, int second);
class Program
{
static long Sum(int x, int y)
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(100);
return x + y;
}
static void CallWhenDone(IAsyncResult iar)
{
Console.WriteLine(" Inside CallWhenDone");
AsyncResult ar = (AsyncResult)iar; // 获取类对象的引用
MyDel del = (MyDel)ar.AsyncDelegate; // 获取委托的引用
long sum = del.EndInvoke(iar); // 调用EndInvoke方法
Console.WriteLine($"Result:{sum}");
}
static void Main()
{
MyDel del = new MyDel(Sum);
Console.WriteLine("Before BeginInvoke");
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone), null);
Console.WriteLine("Doing more work in Main");
Thread.Sleep(500);
Console.WriteLine("Done with Main. Exiting.");
}
}
}