您好,登錄后才能下訂單哦!
如何實(shí)現(xiàn)C#異步的APM模式異步程序開發(fā)?針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡(jiǎn)單易行的方法。
C#已有10多年歷史,單從微軟2年一版的更新進(jìn)度來(lái)看活力異常旺盛,C#中的異步編程也經(jīng)歷了多個(gè)版本的演化,從今天起著手寫一個(gè)系列博文,記錄一下C#中的異步編程的發(fā)展歷程。
我是2004年接觸并使用C#的,那時(shí)C#版本為1.1,所以我們就從就那個(gè)時(shí)候談起。那時(shí)在大學(xué)里自己看書寫程序,所寫的程序大都是同步程序,最多啟動(dòng)個(gè)線程........其實(shí)在C#1.1的時(shí)代已有完整的異步編程解決方案,那就是APM(異步編程模型)。如果還有不了解“同步程序、異步程序”的請(qǐng)自行百度哦。
APM異步編程模型最具代表性的特點(diǎn)是:一個(gè)異步功能由以Begin開頭、End開頭的兩個(gè)方法組成。Begin開頭的方法表示啟動(dòng)異步功能的執(zhí)行,End開頭的方法表示等待異步功能執(zhí)行結(jié)束并返回執(zhí)行結(jié)果。下面是一個(gè)模擬的實(shí)現(xiàn)方式(后面將編寫標(biāo)準(zhǔn)的APM模型異步實(shí)現(xiàn)):
public class Worker { public int A { get; set; } public int B { get; set; } private int R { get; set; } ManualResetEvent et; public void BeginWork(Action action) { et = new ManualResetEvent(false); new Thread(() => { R = A + B; Thread.Sleep(1000); et.Set(); if(null != action) { action(); } }).Start(); } public int EndWork() { if(null == et) { t hrow new Exception("調(diào)用EndWork前,需要先調(diào)用BeginWork"); } else { et.WaitOne(); return R; } } }
static void Main(string[] args) { Worker w = new Worker(); w.BeginWork(()=> { Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId, w.EndWork()); }); Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); }
在上面的模擬APM模型中我們使用了 Thread、ManualResetEvent,如果你對(duì)多線程和ManualResetEvent不熟悉C#中使用異步編程不可避免的會(huì)涉及到多線程的知識(shí),雖然微軟在Framework中做了很多封裝,但朋友們應(yīng)該掌握其本質(zhì)。
上面模擬的APM異步模型之所以簡(jiǎn)單,是因?yàn)镃#發(fā)展過(guò)程中引入了很多優(yōu)秀的語(yǔ)法規(guī)則。上例我們較多的使用了Lambda表達(dá)式,如果你不熟悉 匿名委托與lambda表達(dá)式可看我之前的Bolg《匿名委托與Lambda表達(dá)式》。上面做了如此多的廣告,下面我們來(lái)看一下標(biāo)準(zhǔn)的APM模型如何實(shí)現(xiàn)異步編程。
IAsyncResult接口
IAsyncResult接口定義了異步功能的狀態(tài),該接口具體屬性及含義如下:
// 表示異步操作的狀態(tài)。 [ComVisible(true)] public interface IAsyncResult { // // 摘要: // 獲取一個(gè)值,該值指示異步操作是否已完成。 // // 返回結(jié)果: // 如果操作已完成,則為 true;否則為 false。 bool IsCompleted { get; } // // 摘要: // 獲取用于等待異步操作完成的 System.Threading.WaitHandle。 // // 返回結(jié)果: // 用于等待異步操作完成的 System.Threading.WaitHandle。 WaitHandle AsyncWaitHandle { get; } // // 摘要: // 獲取一個(gè)用戶定義的對(duì)象,該對(duì)象限定或包含有關(guān)異步操作的信息。 // // 返回結(jié)果: // 一個(gè)用戶定義的對(duì)象,限定或包含有關(guān)異步操作的信息。 object AsyncState { get; } // // 摘要: // 獲取一個(gè)值,該值指示異步操作是否同步完成。 // // 返回結(jié)果: // 如果異步操作同步完成,則為 true;否則為 false。 bool CompletedSynchronously { get; } }
注意:模型示例1中的 ManualResetEvent 繼承自 WaitHandle
APM傳說(shuō)實(shí)現(xiàn)方式
在了解了IAsyncResult接口后,我們來(lái)通過(guò)實(shí)現(xiàn) IAsyncResult 接口的方式完成對(duì)模擬示例的改寫工作,代碼如下:
public class NewWorker { public class WorkerAsyncResult : IAsyncResult { AsyncCallback callback; public WorkerAsyncResult(int a,int b, AsyncCallback callback, object asyncState) { A = a; B = b; state = asyncState; this.callback = callback; new Thread(Count).Start(this); } public int A { get; set; } public int B { get; set; } public int R { get; private set; } private object state; public object AsyncState { get { return state; } } private ManualResetEvent waitHandle; public WaitHandle AsyncWaitHandle { get { if (null == waitHandle) { waitHandle = new ManualResetEvent(false); } return waitHandle; } } private bool completedSynchronously; public bool CompletedSynchronously { get { return completedSynchronously; } } private bool isCompleted; public bool IsCompleted { get { return isCompleted; } } private static void Count(object state) { var result = state as WorkerAsyncResult; result.R = result.A + result.B; Thread.Sleep(1000); result.completedSynchronously = false; result.isCompleted = true; ((ManualResetEvent)result.AsyncWaitHandle).Set(); if (result.callback != null) { result.callback(result); } } } public int Num1 { get; set; } public int Num2 { get; set; } public IAsyncResult BeginWork(AsyncCallback userCallback, object asyncState) { IAsyncResult result = new WorkerAsyncResult(Num1,Num2,userCallback, asyncState); return result; } public int EndWork(IAsyncResult result) { WorkerAsyncResult r = result as WorkerAsyncResult; r.AsyncWaitHandle.WaitOne(); return r.R; } }
示例代碼分析:
上面代碼中NewWorker的內(nèi)部類 WorkerAsyncResult 是關(guān)鍵點(diǎn),它實(shí)現(xiàn)了 IAsyncResult 接口并由它來(lái)負(fù)責(zé)開啟新線程完成計(jì)算工作。
在WorkerAsyncResult中增加了 A、B兩個(gè)私有屬性來(lái)存儲(chǔ)用于計(jì)算的數(shù)值,一個(gè)對(duì)外可讀不可寫的屬性R,用于存儲(chǔ)WorkerAsyncResult內(nèi)部運(yùn)算的結(jié)果。AsyncWaitHandle屬性由 ManualResetEvent 來(lái)充當(dāng),并在首次訪問時(shí)創(chuàng)建ManualResetEvent(但不釋放)。其他接口屬性正常實(shí)現(xiàn),沒有什么可說(shuō)。
WorkerAsyncResult 中新增 static Count 方法,參數(shù) state 為調(diào)用Count方法的當(dāng)前WorkerAsyncResult對(duì)象。Count 方法在 WorkerAsyncResult 對(duì)象的新啟線程中運(yùn)行,因此Thread.Sleep(1000)將阻塞新線程1秒中。然后設(shè)置當(dāng)前WorkerAsyncResult對(duì)象是否同步完成為false,異步完成狀態(tài)為true,釋放ManualResetEvent通知以便等待線程獲取通知進(jìn)入執(zhí)行狀態(tài),判斷是否有異步執(zhí)行結(jié)束回調(diào)委托,存在則回調(diào)之。
NewWorker 非常簡(jiǎn)單,Num1、Num2兩個(gè)屬性為要計(jì)算的數(shù)值。BeginWork 創(chuàng)建WorkerAsyncResult對(duì)象、并將要計(jì)算的兩個(gè)數(shù)值Num1、Num2、userCallback回調(diào)委托、object 類型的 asyncState 傳入要?jiǎng)?chuàng)建的WorkerAsyncResult對(duì)象。經(jīng)過(guò)此步操作,WorkerAsyncResult對(duì)象獲取了運(yùn)算所需的所有數(shù)據(jù)、運(yùn)算完成后的回調(diào),并馬上啟動(dòng)新線程進(jìn)行運(yùn)算(執(zhí)行WorkerAsyncResult.Count方法)。
因?yàn)閃orkerAsyncResult.Count執(zhí)行在新線程中,在該線程外部無(wú)法準(zhǔn)確獲知新線程的狀態(tài)。為了滿足外部線程與新線程同步的需求,在NewWorker中增加EndWork方法,參數(shù)類型為IAsyncResult。要調(diào)用EndWork方法應(yīng)傳入BeginWork 獲取的WorkerAsyncResult對(duì)象,EndWork方法獲取WorkerAsyncResult對(duì)象后,調(diào)用WorkerAsyncResult.AsyncWaitHandle.WaitOne()方法,等待獲取ManualResetEvent通知,在獲取到通知時(shí)運(yùn)算線程已運(yùn)算結(jié)束(線程并未結(jié)束),下一步獲取運(yùn)算結(jié)果R并返回。
接下來(lái)是NewWorker調(diào)用程序,如下:
static void Main(string[] args) { NewWorker w2 = new NewWorker(); w2.Num1 = 10; w2.Num2 = 12; IAsyncResult r = null; r = w2.BeginWork((obj) => { Console.WriteLine("Thread Id:{0},Count:{1}",Thread.CurrentThread.ManagedThreadId, w2.EndWork(r)); }, null); Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); }
下圖我簡(jiǎn)單畫的程序調(diào)用過(guò)程,有助于各位朋友理解:
標(biāo)準(zhǔn)的APM模型異步編程,對(duì)應(yīng)開發(fā)人員來(lái)說(shuō)過(guò)于復(fù)雜。因此通過(guò)實(shí)現(xiàn) IAsyncResult 接口進(jìn)行異步編程,就是傳說(shuō)中的中看不中用(罪過(guò)罪過(guò).....)。
Delegate異步編程(APM 標(biāo)準(zhǔn)實(shí)現(xiàn))
C#中委托天生支持異步調(diào)用(APM模型),任何委托對(duì)象后"."就會(huì)發(fā)現(xiàn)BeginInvoke、EndInvoke、Invoke三個(gè)方法。BeginInvoke為異步方式調(diào)用委托、EndInvoke等待委托的異步調(diào)用結(jié)束、Invoke同步方式調(diào)用委托。因此上面的標(biāo)準(zhǔn)APM實(shí)例,可借助 delegate 進(jìn)行如下簡(jiǎn)化。
上面NewWorker使用委托方式改寫如下:
public class NewWorker2 { Func<int, int, int> action; public NewWorker2() { action = new Func<int, int, int>(Work); } public IAsyncResult BeginWork(AsyncCallback callback, object state) { dynamic obj = state; return action.BeginInvoke(obj.A, obj.B, callback, this); } public int EndWork(IAsyncResult asyncResult) { try { return action.EndInvoke(asyncResult); } catch (Exception ex) { throw ex; } } private int Work(int a, int b) { Thread.Sleep(1000); return a + b; } }
調(diào)用程序:
static void Main(string[] args) { NewWorker2 w2 = new NewWorker2(); IAsyncResult r = null; r = w2.BeginWork((obj) => { Console.WriteLine("Thread Id:{0},Count:{1}", Thread.CurrentThread.ManagedThreadId, w2.EndWork(r)); }, new { A = 10, B = 11 }); Console.WriteLine("Thread Id:{0}", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); }
上面的使用委托進(jìn)行APM異步編程,比實(shí)現(xiàn) IAsyncResult 接口的方式精簡(jiǎn)太多、更易理解使用。因此這里建議朋友們 delegate 異步調(diào)用模型應(yīng)該掌握起來(lái),而通過(guò)實(shí)現(xiàn) IAsyncResult
關(guān)于如何實(shí)現(xiàn)C#異步的APM模式異步程序開發(fā)問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注億速云行業(yè)資訊頻道了解更多相關(guān)知識(shí)。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。