C#异步编程模型
什么是異步編程模型
異步編程模型(Asynchronous Programming Model,簡(jiǎn)稱APM)是C#1.1支持的一種實(shí)現(xiàn)異步操作的編程模型,雖然已經(jīng)比較“古老”了,但是依然可以學(xué)習(xí)一下的。通過(guò)對(duì)APM的學(xué)習(xí),我總結(jié)了以下三點(diǎn):
1. APM的本質(zhì)是使用委托和線程池來(lái)實(shí)現(xiàn)異步編程的。
2.?實(shí)現(xiàn)APM的關(guān)鍵是要實(shí)現(xiàn)IAsyncResult接口。
3. 實(shí)現(xiàn)了APM的類都會(huì)定義一對(duì)形如BeginXXX()和EndXXX()的方法,例如,FileStream類定義了BeginRead()方法和EndRead()方法,可以實(shí)現(xiàn)異步讀取文件內(nèi)容。
下面我們就通過(guò)具體的代碼來(lái)實(shí)現(xiàn)異步編程模型。
實(shí)現(xiàn)異步編程模型
1. 實(shí)現(xiàn)IAsyncResult接口
IAsyncResult接口是C#類庫(kù)中定義的一個(gè)接口,表示異步操作的狀態(tài),具體介紹可以查看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接口聲明的四個(gè)屬性:
1. AsyncState屬性是一個(gè)用戶定義的對(duì)象,包含異步操作狀態(tài)信息。例如,當(dāng)我們調(diào)用FileStream類的BeginRead()方法進(jìn)行異步讀取文件內(nèi)容時(shí),傳入的最后一個(gè)參數(shù)對(duì)應(yīng)的就是AsyncState屬性。
2. AsyncWaitHandle屬性主要的作用是阻塞當(dāng)前線程來(lái)等待異步操作完成。WaitHandle抽象類,有一個(gè)很重要的派生類ManualResetEvent。
3. CompletedSynchronously屬性比較特別,用來(lái)判斷異步操作是否是同步完成(這個(gè)有點(diǎn)兒繞~)。
4. IsCompleted屬性就比較簡(jiǎn)單了,用來(lái)判斷異步操作是否完成,true表示已完成,false表示還未完成。
在實(shí)現(xiàn)IAsyncResult接口時(shí),我們主要會(huì)用到AsyncState,IsCompleted和AsyncWaitHandle屬性。
1 /// <summary>2 /// CalculatorAsyncResult<T>類,實(shí)現(xiàn)了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 //我們傳入的異步回調(diào)方法16 private AsyncCallback _asyncCallback;17 18 //保存異步操作返回結(jié)果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 /// 調(diào)用異步回調(diào)方法70 /// </summary>71 private void InvokeAsyncCallback()72 {73 _isCompleted = true;74 75 if (_waitHandle != null)76 {77 _waitHandle.Set();78 }79 80 //調(diào)用我們傳入的異步回調(diào)方法81 _asyncCallback(this);82 }83 84 /// <summary>85 /// 執(zhí)行異步工作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()方法
下面就來(lái)定義我們自己的APM接口和具體實(shí)現(xiàn)類,編寫(xiě)B(tài)eginXXX()和EndXXX()方法。
1 /// <summary>2 /// 異步計(jì)算接口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 /// 異步計(jì)算接口實(shí)現(xiàn)類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 /// 計(jì)算方法 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. 獲取異步操作結(jié)果
APM提供了四種獲取異步操作的結(jié)果方式供我們選擇:
1.?通過(guò)IAsyncResult的AsyncWaitHandle屬性,調(diào)用它的WaitOne()方法使調(diào)用線程阻塞來(lái)等待異步操作完成再調(diào)用EndXXX()方法來(lái)獲取異步操作結(jié)果。
2. 在調(diào)用BeginXXX()方法的線程上調(diào)用EndXXX()方法來(lái)獲取異步操作結(jié)果。這種方式也會(huì)阻塞調(diào)用線程(阻塞原理同方式1,具體在上面的代碼中有體現(xiàn))。
3. 輪詢IAsyncResult的IsComplete屬性,當(dāng)異步操作完成后再調(diào)用EndXXX()方法來(lái)獲取異步操作結(jié)果。
4.?使用 AsyncCallback委托來(lái)指定異步操作完成時(shí)要回調(diào)的方法,在回調(diào)方法中調(diào)用EndXXX()方法來(lái)獲取異步操作結(jié)果。
在上述的四種方式中,只有第四種方式是完全不會(huì)阻塞調(diào)用線程的,所以多數(shù)情況下我們都會(huì)選擇回調(diào)的方式來(lái)獲取異步操作結(jié)果。
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 /// 我們定義的回調(diào)方法 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 }運(yùn)行結(jié)果:
至此,我們已經(jīng)完整地實(shí)現(xiàn)了APM異步編程模型,從運(yùn)行結(jié)果中我們可以得出,通過(guò)回調(diào)的方式來(lái)獲取異步操作結(jié)果是完全不會(huì)阻塞調(diào)用線程的。
總結(jié)
1. 實(shí)現(xiàn)APM的關(guān)鍵是實(shí)現(xiàn)IAsyncResult接口。在IAsyncResult實(shí)現(xiàn)類中,需要使用線程池來(lái)異步地執(zhí)行操作,在操作完成之后,再調(diào)用傳入的回調(diào)方法來(lái)返回操作結(jié)果。
2. 實(shí)現(xiàn)了APM的類中都會(huì)定義一對(duì)BeginXXX()和EndXXX()方法,開(kāi)始異步操作,結(jié)束異步操作并返回異步操作結(jié)果。
3. 獲取異步操作結(jié)果有四種方式,但是只有回調(diào)方式是完全不會(huì)阻塞調(diào)用線程的,其他的都會(huì)阻塞調(diào)用線程。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎(jiǎng)勵(lì)來(lái)咯,堅(jiān)持創(chuàng)作打卡瓜分現(xiàn)金大獎(jiǎng)總結(jié)
- 上一篇: 注销信用卡能消除不良信用记录吗
- 下一篇: C# async 和 await 理解