C#非同步之APM模式非同步程式開發的範例分享

黄舟
發布: 2018-05-11 11:32:56
原創
1909 人瀏覽過

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<br/>APM傳說實作方式<br/>在了解了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方法應傳入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使用委托方式改写如下:

<br/>
登入後複製

<br/>

    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 接口的传说方式看你的喜好吧。 

以上是C#非同步之APM模式非同步程式開發的範例分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!