C#异步编程模型

什么是异步编程模型

异步编程模型(Asynchronous Programming Model,简称APM)是C#1.1支持的一种实现异步操作的编程模型,虽然已经比较“古老”了,但是依然可以学习一下的。通过对APM的学习,我总结了以下三点:

1. APM的本质是使用委托和线程池来实现异步编程的。

2. 实现APM的关键是要实现IAsyncResult接口

3. 实现了APM的类都会定义一对形如BeginXXX()和EndXXX()的方法,例如,FileStream类定义了BeginRead()方法和EndRead()方法,可以实现异步读取文件内容。

下面我们就通过具体的代码来实现异步编程模型。

实现异步编程模型

1. 实现IAsyncResult接口

IAsyncResult接口是C#类库中定义的一个接口,表示异步操作的状态,具体介绍可以查看MSDN。

 1  public interface IAsyncResult
 2 {
 3     object AsyncState { get; }
 4     
 5     WaitHandle AsyncWaitHandle { get; }
 6     
 7     bool CompletedSynchronously { get; }
 8 
 9     bool IsCompleted { get; }
10 }

上面的代码是IAsyncResult接口声明的四个属性:

1. AsyncState属性是一个用户定义的对象,包含异步操作状态信息。例如,当我们调用FileStream类的BeginRead()方法进行异步读取文件内容时,传入的最后一个参数对应的就是AsyncState属性。

2. AsyncWaitHandle属性主要的作用是阻塞当前线程来等待异步操作完成。WaitHandle抽象类,有一个很重要的派生类ManualResetEvent。

3. CompletedSynchronously属性比较特别,用来判断异步操作是否是同步完成(这个有点儿绕~)。

4. IsCompleted属性就比较简单了,用来判断异步操作是否完成,true表示已完成,false表示还未完成。

在实现IAsyncResult接口时,我们主要会用到AsyncState,IsCompleted和AsyncWaitHandle属性。

  1 /// 
  2 /// CalculatorAsyncResult类,实现了IAsyncResult接口
  3 /// 
  4 /// 
  5 public class CalculatorAsyncResult : IAsyncResult
  6 {
  7     private ManualResetEvent _waitHandle;
  8 
  9     private object _asyncState;
 10 
 11     private bool _completedSynchronously;
 12 
 13     private bool _isCompleted;
 14 
 15     //我们传入的异步回调方法
 16     private AsyncCallback _asyncCallback;
 17 
 18     //保存异步操作返回结果
 19     public T CalulatorResult { get; set; }
 20 
 21     public static CalculatorAsyncResult CreateCalculatorAsyncResult(Func work, AsyncCallback asyncCallback, object obj)
 22     {
 23         var asyncResult = new CalculatorAsyncResult(obj, asyncCallback, false, false);
 24 
 25         asyncResult.ExecuteWork(work);
 26 
 27         return asyncResult;
 28     }
 29 
 30     public CalculatorAsyncResult(object obj, AsyncCallback asyncCallback, bool completedSynchronously, bool isCompleted)
 31     {
 32         _waitHandle = new ManualResetEvent(false);
 33 
 34         _asyncState = obj;
 35 
 36         _completedSynchronously = completedSynchronously;
 37 
 38         _isCompleted = isCompleted;
 39 
 40         _asyncCallback = asyncCallback;
 41     }
 42 
 43     public object AsyncState
 44     { 
 45         get { return _asyncState; } 
 46     }
 47 
 48     public WaitHandle AsyncWaitHandle
 49     {
 50         get{ return _waitHandle; }
 51     }
 52 
 53     public bool CompletedSynchronously
 54     {
 55         get { return _completedSynchronously; }
 56     }
 57 
 58     public bool IsCompleted
 59     {
 60         get { return _isCompleted; }
 61     }
 62 
 63     public void Wait()
 64     {
 65         _waitHandle.WaitOne();
 66     }
 67 
 68     /// 
 69     /// 调用异步回调方法
 70     /// 
 71     private void InvokeAsyncCallback()
 72     {
 73         _isCompleted = true;
 74 
 75         if (_waitHandle != null)
 76         {
 77             _waitHandle.Set();
 78         }
 79         
 80         //调用我们传入的异步回调方法
 81         _asyncCallback(this);
 82     }
 83 
 84     /// 
 85     /// 执行异步工作
 86     /// 
 87     /// 
 88     public void ExecuteWork(Func work)
 89     {
 90         if(_asyncCallback != null)
 91         {
 92             Task task = Task.Factory.StartNew(work);
 93 
 94             task.ContinueWith(t => 
 95             {
 96                 CalulatorResult = t.Result;
 97 
 98                 InvokeAsyncCallback();
 99             });
100         }
101         else 
102         {
103             _isCompleted = true;
104 
105             if(_waitHandle != null)
106             {
107                 _waitHandle.Set();
108             }
109         }
110     }
111 }

2. 定义BeginXXX()和EndXXX()方法

下面就来定义我们自己的APM接口和具体实现类,编写BeginXXX()和EndXXX()方法。

 1 /// 
 2 /// 异步计算接口
 3 /// 
 4 /// 
 5  public interface ICalculator
 6  {
 7      IAsyncResult BeginAdd(T x, T y, AsyncCallback asyncCallback, Object obj);
 8 
 9      T EndAdd(IAsyncResult ar);
10  }
 1 /// 
 2 /// 异步计算接口实现类
 3 /// 
 4  public class Calculator : ICalculator<double>
 5 {
 6     public IAsyncResult BeginAdd(double x, double y, AsyncCallback asyncCallback, Object obj)
 7     {
 8         return CalculatorAsyncResult<double>.CreateCalculatorAsyncResult(delegate { return Add(x, y); }, asyncCallback, obj);
 9     }
10 
11     public double EndAdd(IAsyncResult ar)
12     {
13         var calculatorAsyncResult = (CalculatorAsyncResult<double>)(ar);
14 
15         calculatorAsyncResult.Wait();
16 
17         return calculatorAsyncResult.CalulatorResult;
18     }
19 
20     /// 
21     /// 计算方法
22     /// 
23     /// 
24     /// 
25     /// 
26     protected double Add(double x, double y)
27     {
28         Console.WriteLine("Async thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId);
29 
30         Console.WriteLine("Async thread(id={0}) is calculating...\n", Thread.CurrentThread.ManagedThreadId);
31 
32         Thread.Sleep(3000);
33 
34         var r = x + y;
35 
36         Console.WriteLine("Async thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
37 
38         return r;
39     }
40 }

3. 获取异步操作结果

APM提供了四种获取异步操作的结果方式供我们选择:

1. 通过IAsyncResult的AsyncWaitHandle属性,调用它的WaitOne()方法使调用线程阻塞来等待异步操作完成再调用EndXXX()方法来获取异步操作结果。

2. 在调用BeginXXX()方法的线程上调用EndXXX()方法来获取异步操作结果。这种方式也会阻塞调用线程(阻塞原理同方式1,具体在上面的代码中有体现)。

3. 轮询IAsyncResult的IsComplete属性,当异步操作完成后再调用EndXXX()方法来获取异步操作结果。

4. 使用 AsyncCallback委托来指定异步操作完成时要回调的方法,在回调方法中调用EndXXX()方法来获取异步操作结果。

在上述的四种方式中,只有第四种方式是完全不会阻塞调用线程的,所以多数情况下我们都会选择回调的方式来获取异步操作结果。

 1  public class Program
 2 {
 3     public static double result = 0;
 4 
 5     static void Main(string[] args)
 6     {
 7         Console.WriteLine("Main thread(id={0}) begins.\n", Thread.CurrentThread.ManagedThreadId);
 8 
 9         var calculator = new Calculator();
10 
11         Console.WriteLine("Main thread(id={0}) invokes BeginAdd() function.\n", Thread.CurrentThread.ManagedThreadId);
12 
13         calculator.BeginAdd(1, 2, Callback, calculator);
14 
15         Console.WriteLine("Main thread(id={0}) is sleeping...\n", Thread.CurrentThread.ManagedThreadId);
16 
17         Thread.Sleep(5000);
18 
19         Console.WriteLine("The calculating result of async operation is {0}.\n", result);
20 
21         Console.WriteLine("Main thread(id={0}) ends.\n", Thread.CurrentThread.ManagedThreadId);
22     }
23 
24     /// 
25     /// 我们定义的回调方法
26     /// 
27     /// 
28     public static void Callback(IAsyncResult ar)
29     {
30         var calculator = (Calculator)(ar.AsyncState);
31 
32         result = calculator.EndAdd(ar);
33     }
34 }

运行结果:

C#异步编程模型_第1张图片

至此,我们已经完整地实现了APM异步编程模型,从运行结果中我们可以得出,通过回调的方式来获取异步操作结果是完全不会阻塞调用线程的。

总结

1. 实现APM的关键是实现IAsyncResult接口。在IAsyncResult实现类中,需要使用线程池来异步地执行操作,在操作完成之后,再调用传入的回调方法来返回操作结果。

2. 实现了APM的类中都会定义一对BeginXXX()和EndXXX()方法,开始异步操作,结束异步操作并返回异步操作结果。

3. 获取异步操作结果有四种方式,但是只有回调方式是完全不会阻塞调用线程的,其他的都会阻塞调用线程。

转载于:https://www.cnblogs.com/smart-monkey/p/7295738.html

你可能感兴趣的:(c#)