一、使用BeginInvoke和EndInvoke进行异步调用的四种常用方法,每种方法都是通过BeginInvoke方法来启动异步方法,调用EndInvoke方法来完成异步调用。
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
namespace ConsoleApplication1
{
public delegate void AsynComputeCaller(int num, out int result);
class Factorial
{
public int Compute(int num)
{
Thread.Sleep(new Random().Next(100,999));
if (num == 1) return 1;
return num * this.Compute(num - 1);
}
/// <summary>
/// 异步计算阶乘
/// </summary>
/// <param name="num"></param>
/// <param name="result"></param>
public void AsynCompute(int num, out int result)
{
Console.WriteLine("\n AsynCompute...start");
DateTime startTime = DateTime.Now;
result = this.Compute(num);
DateTime endTime = DateTime.Now;
Console.WriteLine("\n AsynCompute...end(Milliseconds Used:{0})",
endTime.Subtract(startTime).Milliseconds);
}
}
class Program
{
int result = 0;
/// <summary>
/// 调用BeginInvoke方法启动异步方法,
/// 进行某些操作,
/// 然后调用EndInvoke方法来一直阻止请求线程到调用完成。
/// </summary>
void Run()
{
Factorial f = new Factorial();
AsynComputeCaller caller = new AsynComputeCaller(f.AsynCompute);
Console.WriteLine("Run...start");
IAsyncResult iAsyncResult = caller.BeginInvoke(4, out result, null, null);
//主线程进行一些操作
Console.WriteLine(" 主线程进行一些操作");
for (int i = 0; i < 50; i++)
{
Thread.Sleep(50);
Console.Write(".");
}
Console.WriteLine();
//调用EndInvoke来等待异步调用结束,并获得结果
caller.EndInvoke(out result, iAsyncResult);
Console.Write("Run...end(result:{0})", result);
}
/// <summary>
/// 调用BeginInvoke方法启动异步方法,
/// 使用System.IAsyncResult.AsyncWaitHandle属性获取WaitHandle,
/// 使用它的WaitOne方法一直阻止执行直到发出WaitHandle信号,
/// 然后调用EndInvoke方法。
/// </summary>
void Run2()
{
Factorial f = new Factorial();
AsynComputeCaller caller = new AsynComputeCaller(f.AsynCompute);
Console.WriteLine("Run...start");
IAsyncResult iAsyncResult = caller.BeginInvoke(12, out result, null, null);
//主线程进行一些操作
Console.WriteLine(" 主线程进行一些操作");
for (int i = 0; i < 50; i++)
{
Thread.Sleep(50);
Console.Write(".");
}
Console.WriteLine();
Console.WriteLine(" 等待WaitHandle接收到信号");
//等待WaitHandle接收到信号
iAsyncResult.AsyncWaitHandle.WaitOne();
caller.EndInvoke(out result, iAsyncResult);
Console.Write("Run...end(result:{0})", result);
}
/// <summary>
/// 调用BeginInvoke方法启动异步方法,
/// 轮询由BeginInvoke返回的IAsyncResult,
/// 确定异步调用何时完成,
/// 然后调用EndInvoke。
/// </summary>
void Run3()
{
Factorial f = new Factorial();
AsynComputeCaller caller = new AsynComputeCaller(f.AsynCompute);
Console.WriteLine("Run...start");
IAsyncResult iAsyncResult = caller.BeginInvoke(6, out result, null, null);
//主线程进行一些操作
Console.WriteLine(" 主线程进行一些操作");
while(!iAsyncResult.IsCompleted)
{
Thread.Sleep(100);
Console.Write(".");
}
Console.WriteLine();
Console.WriteLine(" 异步方法已经结束");
//等待WaitHandle接收到信号
iAsyncResult.AsyncWaitHandle.WaitOne();
caller.EndInvoke(out result, iAsyncResult);
Console.Write("Run...end(result:{0})", result);
}
/// <summary>
/// 调用BeginInvoke方法启动异步方法时,
/// 将代表异步方法完成时需要回调的方法的委托传递给BeginInvoke。
/// 异步调用完成后,将在ThreadPool线程上执行该回调方法。
/// 在该回调方法中调用EndInvoke。
/// </summary>
void Run4()
{
Factorial f = new Factorial();
AsynComputeCaller caller = new AsynComputeCaller(f.AsynCompute);
Console.WriteLine("Run...start");
IAsyncResult iAsyncResult = caller.BeginInvoke(6, out result,
new AsyncCallback(CallbackMethod), caller);
//主线程进行一些操作
Console.WriteLine(" 主线程进行一些操作");
for (int i = 0; i < 50; i++)
{
Thread.Sleep(50);
Console.Write(".");
}
Console.WriteLine();
}
void CallbackMethod(IAsyncResult ar)
{
AsynComputeCaller caller = (AsynComputeCaller)ar.AsyncState;
caller.EndInvoke(out result, ar);
Console.Write("Run...end(result:{0})", result);
}
static void Main(string[] args)
{
Program p = new Program();
Console.WriteLine("\n\n*** Run ***");
p.Run();
Console.WriteLine("\n\n*** Run2 ***");
p.Run2();
Console.WriteLine("\n\n*** Run3 ***");
p.Run3();
Console.WriteLine("\n\n*** Run4 ***");
p.Run4();
Console.Read();
}
}
}
二、异步编程(AsyncCallback委托,IAsyncResult接口,BeginInvoke方法,EndInvoke方法的使用小总结)
让我们来看看同步异步的区别:
同步方法调用在程序继续执行之前需要等待同步方法执行完毕返回结果
异步方法则在被调用之后立即返回以便程序在被调用方法完成其任务的同时执行其它操作
.NET框架基类库中有好几种类都可以提供同步和异步的方法调用。
因为同步方法调用会导致程序流程中途等待,所以采用同步方法的情况下往往会导致程序执行的延迟
相比来说,在某些条件下选择异步方法调用就可能更好一些
例如,有的时候程序需要给多个Web服务发出请求,还有远程处理信道(HTTP、TCP)和代理,这时就最好采用异步方法
.NET Framework允许异步调用任何方法,定义与需要调用的方法具有相同签名的委托
CLR将自动为该委托定义添加适当签名的BeginInvoke虚方法和EndInvoke虚方法和Invoke方法。
我们先来了解这2个方法和一个委托和一个接口:
(1)
BeginInvoke 方法可启动异步调用。
它与您需要异步执行的方法具有相同的参数,另外它还有两个可选参数。第一个参数是一个 AsyncCallback 委托,该委托引用在异步调用完成时要调用的方法。第二个参数是一个用户定义的对象,该对象可向回调方法传递信息。BeginInvoke 立即返回,不等待异步调用完成。BeginInvoke 会返回 IAsyncResult,这个结果可用于监视异步调用进度。
结果对象IAsyncResult是从开始操作返回的,并且可用于获取有关异步开始操作是否已完成的状态。
结果对象被传递到结束操作,该操作返回调用的最终返回值。
在开始操作中可以提供可选的回调。如果提供回调,在调用结束后,将调用该回调;并且回调中的代码可以调用结束操作。
(2)
EndInvoke 方法检索异步调用的结果。
调用 BeginInvoke 后可随时调用 EndInvoke 方法;如果异步调用尚未完成,EndInvoke 将一直阻止调用线程,直到异步调用完成后才允许调用线程执行。EndInvoke 的参数包括您需要异步执行的方法的 out 和 ref 参数以及由 BeginInvoke 返回的 IAsyncResult。
(3)
AsyncCallback委托用于指定在开始操作完成后应被调用的方法
AsyncCallback委托被作为开始操作上的第二个到最后一个参数传递
代码原型如下:
[Serializable]
public delegate void AsyncCallback(IAsyncResult ar);
AsyncCallback为客户端应用程序提供完成异步操作的方法。开始异步操作时,该回调委托被提供给客户端。AsyncCallback 引用的事件处理程序包含完成客户端异步任务的程序逻辑。
AsyncCallback 使用 IAsyncResult 接口获取异步操作的状态。
(4)
IAsyncResult接口
它表示异步操作的状态.
该接口定义了4个公用属性
代码原型如下:
public interface IAsyncResult
实际上,发起和完成.NET异步调用有4种方案可供你选择
1.方案1-使用EndInvoke等待异步调用
异步执行方法最简单的方式是通过调用委托的 BeginInvoke 方法来开始执行方法,在主线程上执行一些工作,然后调用委托的 EndInvoke 方法。EndInvoke 可能会阻止调用线程,因为它直到异步调用完成之后才返回。这种技术非常适合文件或网络操作,但是由于 EndInvoke 会阻止它,所以不要从服务于用户界面的线程中调用它。
还是先来段自己喜欢的控制台代码:
2
3namespace ConsoleApplication1
4{
5 class Class1
6 {
7 public delegate void AsyncEventHandler();
8
9 void Event1()
10 {
11 Console.WriteLine("Event1 Start");
12 System.Threading.Thread.Sleep(2000);
13 Console.WriteLine("Event1 End");
14 }
15
16 void Event2()
17 {
18 Console.WriteLine("Event2 Start");
19 int i=1;
20 while(i<1000)
21 {
22 i=i+1;
23 Console.WriteLine("Event2 "+i.ToString());
24 }
25 Console.WriteLine("Event2 End");
26 }
27
28 void CallbackMethod(IAsyncResult ar)
29 {
30 ((AsyncEventHandler) ar.AsyncState).EndInvoke(ar);
31 }
32
33
34 [STAThread]
35 static void Main(string[] args)
36 {
37 long start=0;
38 long end=0;
39 Class1 c = new Class1();
40 Console.WriteLine("ready");
41 start=DateTime.Now.Ticks;
42
43 AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
44 IAsyncResult ia=asy.BeginInvoke(null,null);
45 c.Event2();
46 asy.EndInvoke(ia);
47
48 end =DateTime.Now.Ticks;
49 Console.WriteLine("时间刻度差="+ Convert.ToString(end-start) );
50 Console.ReadLine();
51 }
52 }
53}
54
2.方案2-轮询异步调用完成
由 BeginInvoke 返回的IAsyncResult.IsCompleted属性获取异步操作是否已完成的指示,发现异步调用何时完成.
从用户界面的服务线程中进行异步调用时可以执行此操作。轮询完成允许调用线程在异步调用在 ThreadPool 线程上执行时继续执行。
再次修改代码43-46这几行代码:
AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
IAsyncResult ia=asy.BeginInvoke(null,null);
c.Event2();
while(!ia.IsCompleted)
{
}
asy.EndInvoke(ia);
3.方案3-使用WaitHandle等待异步调用
IAsyncResult.AsyncWaitHandle属性获取用于等待异步操作完成的WaitHandle
WaitHandle.WaitOne方法阻塞当前线程,直到当前的WaitHandle收到信号
使用WaitHandle,则在异步调用完成之后,但在通过调用EndInvoke结果之前,可以执行其他处理
再次修改代码43-46这几行代码:
AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
IAsyncResult ia=asy.BeginInvoke(null,null);
c.Event2();
ia.AsyncWaitHandle.WaitOne();
4.方案4-异步调用完成时执行回调方法
如果启动异步调用的线程不需要是处理结果的线程,则可以在调用完成时执行回调方法。回调方法在 ThreadPool 线程上执行。
若要使用回调方法,必须将引用回调方法的 AsyncCallback 委托传递给 BeginInvoke。也可以传递包含回调方法将要使用的信息的对象。例如,可以传递启动调用时曾使用的委托,以便回调方法能够调用 EndInvoke。
再次修改代码43-46这几行代码:
AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
asy.BeginInvoke(new AsyncCallback(c.CallbackMethod),asy);
c.Event2();
总结:
四种使用BeginInvoke和EndInvoke进行异步调用的常用方法。在调用BeginInvoke 之后,可以执行下列操作:
1.进行某些操作,然后调用 EndInvoke 一直阻止到调用完成。
2.使用 System.IAsyncResult.AsyncWaitHandle 属性获取 WaitHandle,使用它的 WaitOne 方法一直阻止执行直到发出 WaitHandle 信号,然后调用 EndInvoke。
3.轮询由 BeginInvoke 返回的 IAsyncResult,确定异步调用何时完成,然后调用 EndInvoke。
4.将用于回调方法的委托传递给 BeginInvoke。异步调用完成后,将在 ThreadPool 线程上执行该方法。该回调方法将调用 EndInvoke。
注意:
每次都要调用EndInvoke来完成异步调用。