C#非同步之APM模式非同步程式開發的樣本分享

來源:互聯網
上載者:User
C#已有10多年歷史,單從微軟2年一版的更新進度來看活力異常旺盛,C#中的非同步編程也經曆了多個版本的演化,從今天起著手寫一個系列博文,記錄一下C#中的非同步編程的發展曆程。 廣告一下:喜歡我文章的朋友,請點下面的“關注我”。謝謝

我是2004年接觸並使用C#的,那時C#版本為1.1,所以我們就從就那個時候談起。那時在大學裡自己看書寫程式,所寫的程式大都是同步程式,最多啟動個線程........其實在C#1.1的時代已有完整的非同步編程解決方案,那就是APM(非同步編程模型)。如果還有不瞭解“同步程式、非同步程式”的請自行百度哦。

APM非同步編程模型最具代表性的特點是:一個非同步功能由以Begin開頭、End開頭的兩個方法組成。Begin開頭的方法表示啟動非同步功能的執行,End開頭的方法表示等待非同步功能執行結束並返回執行結果。下面是一個類比的實現方式(後面將編寫標準的APM模型非同步實現):

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("調用EndWork前,需要先調用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,如果你對多線程和ManualResetEvent不熟悉C#中使用非同步編程不可避免的會涉及到多線程的知識,雖然微軟在Framework中做了很多封裝,但朋友們應該掌握其本質。

上面類比的APM非同步模型之所以簡單,是因為C#發展過程中引入了很多優秀的文法規則。上例我們較多的使用了Lambda運算式,如果你不熟悉 匿名委託與lambda運算式可看我之前的Bolg《匿名委託與Lambda運算式》。上面做了如此多的廣告,下面我們來看一下標準的APM模型如何?非同步編程。

IAsyncResult介面

IAsyncResult介面定義了非同步功能的狀態,該介面具體屬性及含義如下:

   //     表示非同步作業的狀態。    [ComVisible(true)]    public interface IAsyncResult    {        //        // 摘要:        //     擷取一個值,該值指示非同步作業是否已完成。        //        // 返回結果:        //     如果操作已完成,則為 true;否則為 false。        bool IsCompleted { get; }        //        // 摘要:        //     擷取用於等待非同步作業完成的 System.Threading.WaitHandle。        //        // 返回結果:        //     用於等待非同步作業完成的 System.Threading.WaitHandle。        WaitHandle AsyncWaitHandle { get; }        //        // 摘要:        //     擷取一個使用者定義物件,該對象限定或包含有關非同步作業的資訊。        //        // 返回結果:        //     一個使用者定義物件,限定或包含有關非同步作業的資訊。        object AsyncState { get; }        //        // 摘要:        //     擷取一個值,該值指示非同步作業是否同步完成。        //        // 返回結果:        //     如果非同步作業同步完成,則為 true;否則為 false。        bool CompletedSynchronously { get; }    }

注意:模型樣本1中的 ManualResetEvent 繼承自 WaitHandle
APM傳說實現方式
在瞭解了IAsyncResult介面後,我們來通過實現 IAsyncResult 介面的方式完成對類比樣本的改寫工作,代碼如下:

    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的內部類 WorkerAsyncResult 是關鍵點,它實現了 IAsyncResult 介面並由它來負責開啟新線程完成計算工作。

在WorkerAsyncResult中增加了 A、B兩個私人屬性來儲存用於計算的數值,一個對外可讀不可寫的屬性R,用於儲存WorkerAsyncResult內部運算的結果。AsyncWaitHandle屬性由 ManualResetEvent 來充當,並在首次訪問時建立ManualResetEvent(但不釋放)。其他介面屬性正常實現,沒有什麼可說。

WorkerAsyncResult 中新增 static Count 方法,參數 state 為調用Count方法的當前WorkerAsyncResult對象。Count 方法在 WorkerAsyncResult 對象的新啟線程中運行,因此Thread.Sleep(1000)將阻塞新線程1秒中。然後設定當前WorkerAsyncResult對象是否同步完成為false,非同步完成狀態為true,釋放ManualResetEvent通知以便等待線程擷取通知進入執行狀態,判斷是否有非同步執行結束回調委託,存在則回調之。

NewWorker 非常簡單,Num1、Num2兩個屬性為要計算的數值。BeginWork 建立WorkerAsyncResult對象、並將要計算的兩個數值Num1、Num2、userCallback回調委託、object 類型的 asyncState 傳入要建立的WorkerAsyncResult對象。經過此步操作,WorkerAsyncResult對象擷取了運算所需的所有資料、運算完成後的回調,並馬上啟動新線程進行運算(執行WorkerAsyncResult.Count方法)。

因為WorkerAsyncResult.Count執行在新線程中,在該線程外部無法準確獲知新線程的狀態。為了滿足外部線程與新線程同步的需求,在NewWorker中增加EndWork方法,參數類型為IAsyncResult。要調用EndWork方法應傳入BeginWork 擷取的WorkerAsyncResult對象,EndWork方法擷取WorkerAsyncResult對象後,調用WorkerAsyncResult.AsyncWaitHandle.WaitOne()方法,等待擷取ManualResetEvent通知,在擷取到通知時運算線程已運算結束(線程並未結束),下一步擷取運算結果R並返回。

接下來是NewWorker調用程式,如下:

        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();        }

我簡單畫的程式調用過程,有助於各位朋友理解:

標準的APM模型非同步編程,對應開發人員來說過於複雜。因此通過實現 IAsyncResult 介面進行非同步編程,就是傳說中的中看不中用(罪過罪過.....)。

Delegate非同步編程(APM 標準實現)

C#中委託天生支援非同步呼叫(APM模型),任何委派物件後"."就會發現BeginInvoke、EndInvoke、Invoke三個方法。BeginInvoke為非同步方式調用委託、EndInvoke等待委託的非同步呼叫結束、Invoke同步方式調用委託。因此上面的標準APM執行個體,可藉助 delegate 進行如下簡化。

上面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;        }    }

調用程式:

        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();        }

上面的使用委託進行APM非同步編程,比實現 IAsyncResult 介面的方式精簡太多、更易理解使用。因此這裡建議朋友們 delegate 非同步呼叫模型應該掌握起來,而通過實現 IAsyncResult 介面的傳說方式看你的喜好吧。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.