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 IAsyncResult2 {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 /// <summary>2 /// CalculatorAsyncResult<T>類,實現了IAsyncResult接口3 /// </summary>4 /// <typeparam name="T"></typeparam>5 public class CalculatorAsyncResult<T> : IAsyncResult6 {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<T> CreateCalculatorAsyncResult(Func<T> work, AsyncCallback asyncCallback, object obj)22 {23 var asyncResult = new CalculatorAsyncResult<T>(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 AsyncState44 { 45 get { return _asyncState; } 46 }47 48 public WaitHandle AsyncWaitHandle49 {50 get{ return _waitHandle; }51 }52 53 public bool CompletedSynchronously54 {55 get { return _completedSynchronously; }56 }57 58 public bool IsCompleted59 {60 get { return _isCompleted; }61 }62 63 public void Wait()64 {65 _waitHandle.WaitOne();66 }67 68 /// <summary>69 /// 調用異步回調方法70 /// </summary>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 /// <summary>85 /// 執行異步工作86 /// </summary>87 /// <param name="work"></param>88 public void ExecuteWork(Func<T> work)89 {90 if(_asyncCallback != null)91 {92 Task<T> task = Task.Factory.StartNew<T>(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 /// <summary>2 /// 異步計算接口3 /// </summary>4 /// <typeparam name="T"></typeparam>5 public interface ICalculator<T>6 {7 IAsyncResult BeginAdd(T x, T y, AsyncCallback asyncCallback, Object obj);8 9 T EndAdd(IAsyncResult ar); 10 } 1 /// <summary>2 /// 異步計算接口實現類3 /// </summary>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 /// <summary> 21 /// 計算方法 22 /// </summary> 23 /// <param name="x"></param> 24 /// <param name="y"></param> 25 /// <returns></returns> 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 Program2 {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 /// <summary> 25 /// 我們定義的回調方法 26 /// </summary> 27 /// <param name="ar"></param> 28 public static void Callback(IAsyncResult ar) 29 { 30 var calculator = (Calculator)(ar.AsyncState); 31 32 result = calculator.EndAdd(ar); 33 } 34 }運行結果:
至此,我們已經完整地實現了APM異步編程模型,從運行結果中我們可以得出,通過回調的方式來獲取異步操作結果是完全不會阻塞調用線程的。
總結
1. 實現APM的關鍵是實現IAsyncResult接口。在IAsyncResult實現類中,需要使用線程池來異步地執行操作,在操作完成之后,再調用傳入的回調方法來返回操作結果。
2. 實現了APM的類中都會定義一對BeginXXX()和EndXXX()方法,開始異步操作,結束異步操作并返回異步操作結果。
3. 獲取異步操作結果有四種方式,但是只有回調方式是完全不會阻塞調用線程的,其他的都會阻塞調用線程。
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
- 上一篇: 注销信用卡能消除不良信用记录吗
- 下一篇: C# async 和 await 理解