在上一篇文章(什么是.Net的异步机制(Invoke,BeginInvoke,EndInvoke) - step 2 ), 我们已经简单介绍了异步的调用方式, 下面我们来看看异步的核心.
异步的核心: IAsyncResult
Asynchronous Programming Model
整个异步调用过程中都是围绕IAsyncResult来进行的,大家可以看看上篇文章的例子,BeginXXX 返回这个对象,EndXXX接收这个对象来结束当前异步对象,下面我们来看看IAsyncResult 接口成员/和实现此接口的AsyncResult类成员(其中有些在上篇中已经涉及到)
IAsyncResult接口
2 {
3 WaitHandle AsyncWaitHandle { get; } //阻塞一个线程,直到一个或多个同步对象接收到信号
4 Boolean IsCompleted { get; } //判读当前异步是否完成
5 Object AsyncState { get; } //获取额外的参数值,请看上一篇文章的Code 4.3
6 Boolean CompletedSynchronously { get; } //几乎没有使用
7 }
AsyncResult类
2 {
3 //IAsyncResult 的实现
4 public virtual WaitHandle AsyncWaitHandle { get; }
5 public virtual bool IsCompleted { get; }
6 public virtual object AsyncState { get; }
7 public virtual bool CompletedSynchronously { get; }
8
9 // 其他一些重要的属性
10 public bool EndInvokeCalled { get; set; } //检验是否调用了EndInvoke()
11 public virtual object AsyncDelegate { get; } //获取原始的委托对象,可查看上一篇文章中的Code 4.1/4.2/5
12 }
注意:基本上都是只读属性
下面我们来看看异步的执行顺序,并回顾下 IAsyncResult 下各个属性的应用,如果还是不熟悉请看前2篇文章.
Code 1:
2 {
3 static void Main(string[] args)
4 {
5 Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
6
7 AsyncTest test = new AsyncTest();
8 MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
9 //使用回调函数
10 AsyncCallback callback = new AsyncCallback(OnSalaryCallback);
11 IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);
12
13 DoAntherJob();
14 Console.ReadLine(); // 让黑屏等待,不会直接关闭..
15 }
16
17 //开始其他工作.
18 static void DoAntherJob()
19 {
20 Thread.Sleep(1000);//需要1秒才能完成这个工作,注1
21 Console.WriteLine("[(#{1}){0}]:Do Another Job", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
22 }
23
24 static void OnSalaryCallback(IAsyncResult asyncResult)
25 {
26 //通过AsyncState 获取额外的参数.
27 decimal para = (int)asyncResult.AsyncState;
28
29 //通过AsyncDelegate 获取原始的委托对象
30 AsyncResult obj = (AsyncResult)asyncResult;
31 MyThirdAsyncCode.AsyncTest.SalaryEventHandler del =
(MyThirdAsyncCode.AsyncTest.SalaryEventHandler)obj.AsyncDelegate;
32
33 if (asyncResult.IsCompleted)// 判读是否已经调用完成
34 Console.WriteLine("[(#{1}){0}]:Asynchronous Finished.", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
35
36 decimal val = del.EndInvoke(asyncResult);
37
38 Console.WriteLine("[(#{2}){0}]:Output Result:{1}", DateTime.Now.ToString(), val + para, Thread.CurrentThread.ManagedThreadId);
39 }
40 }
41
42 public class AsyncTest
43 {
44 public delegate decimal SalaryEventHandler(decimal salary, int monthCount, decimal bonus); // 对应YearlySalary方法
45 public decimal YearlySalary(decimal salary, int monthCount, decimal bonus)
46 {
47 //模拟耗时/复杂的逻辑计算.
48 Thread.Sleep(3000);//等待3秒,注2
49 return salary * monthCount + bonus;
50 }
51 }
图1
我们看到DoAntherJob 比异步YearlySalary快2秒,看代码中(注1)和(注2),两个线程的执行结果
接下来,我们说说AsyncWaitHandle 属性. 他返回WaitHandle对象(System.Threading.WaitHandle), 他有3个重要的方法. WaitOne / WaitAny / WaitAll ,我们先来说下WaitOne,在Code1代码基础上只是增加了下面红色部分.
1,WaitOne
Code 1.1
IAsyncResult ar = del.BeginInvoke(100000, 15, 100000, callback, 2000);
//阻碍当前线程,直到异步调用结束.
ar.AsyncWaitHandle.WaitOne();
//开始其他工作.
DoAntherJob();
图1.1
执行输出,对比图1我们可以看到执行的次序不一样了(看时间),调用WaitOne,会阻碍当前线程,直到异步完成,才释放当前的线程, WaitOne 提供了时间的重载版本WaitOne(int millisecondsTimeout)/ WaitOne(TimeSpan timeout);来判断阻碍的时间.无参的版本是无限等待的(直到异步调用结束)
2, WaitAll
我们在Code1的代码基础上加上Hello的异步调用(使Main提供多个异步调用),注意红色部分.
Code 1.2
2 {
3 static void Main(string[] args)
4 {
5 Console.WriteLine("[(#{1}){0}]:Asynchronous Start", DateTime.Now.ToString(), Thread.CurrentThread.ManagedThreadId);
6
7 AsyncTest test = new AsyncTest();
8 MyThirdAsyncCode.AsyncTest.SalaryEventHandler del = test.YearlySalary;
9 MyThirdAsyncCode.AsyncTest.AsyncEventHandler asy = test.Hello;
10
11 IAsyncResult salayAsyc = del.BeginInvoke(100000, 15, 100000, OnSalaryCallback, null);
12 IAsyncResult helloAsyc = asy.BeginInvoke("Hello Andy", OnHelloCallback, null);
13 //把所有异步的句柄保存到WaitHandle 对象中
14 WaitHandle[] handles = { salayAsyc.AsyncWaitHandle, helloAsyc.AsyncWaitHandle };
15 //阻碍当前线程,直到所有异步调用结束.
16 WaitHandle.WaitAll(handles);
17
18 //开始其他工作.
19 DoAntherJob();
20 Console.ReadLine(); // 让黑屏等待,不会直接关闭..