同步方法調用在程序繼續執行之前需要等待同步方法執行完畢返回結果 異步方法則在被調用之后立即返回以便程序在被調用方法完成其任務的同時執行其它操作
.NET框架基類庫中有好幾種類都可以提供同步和異步的方法調用。 因為同步方法調用會導致程序流程中途等待,所以采用同步方法的情況下往往會導致程序執行的延遲 相比來說,在某些條件下選擇異步方法調用就可能更好一些 例如,有的時候程序需要給多個Web服務發出請求,還有遠程處理信道(HTTP、TCP)和代理,這時就最好采用異步方法
.NET Framework允許異步調用任何方法,定義與需要調用的方法具有相同簽名的委托 CLR將自動為該委托定義添加適當簽名的BeginInvoke虛方法和EndInvoke虛方法和Invoke方法。
我們先來了解這2個方法和一個委托和一個接口: (1)BeginInvoke方法用於啟動異步調用 它與您需要異步執行的方法具有相同的參數,只不過還有兩個額外的參數,將 AsyncCallback 和 AsyncState(可通過 IAsyncResult 接口的AsyncState 屬性獲得)作為最后兩個參數,如沒有可以為空. BeginInvoke立即返回,不等待異步調用完成。 BeginInvoke返回IasyncResult,可用於監視調用進度。
結果對象IAsyncResult是從開始操作返回的,並且可用於獲取有關異步開始操作是否已完成的狀態。 結果對象被傳遞到結束操作,該操作返回調用的最終返回值。 在開始操作中可以提供可選的回調。如果提供回調,在調用結束后,將調用該回調;並且回調中的代碼可以調用結束操作。
(2)EndInvoke方法用於檢索異步調用結果。 在調用BeginInvoke后可隨時調用EndInvoke方法,注意:始終在異步調用完成后調用EndInvoke. 如果異步調用未完成,EndInvoke將一直阻塞到異步調用完成。 EndInvoke的參數包括需要異步執行的方法的out和ref參數以及由BeginInvoke返回的IAsyncResult。 要注意的是,始終在異步調用完成后調用EndInvoke
(3)AsyncCallback委托用於指定在開始操作完成后應被調用的方法 AsyncCallback委托被作為開始操作上的第二個到最后一個參數傳遞 代碼原型如下: [Serializable] public delegate void AsyncCallback(IAsyncResult ar);
(4)IAsyncResult接口 它表示異步操作的狀態. 該接口定義了4個公用屬性
實際上,發起和完成.NET異步調用有4種方案可供你選擇 1.方案1-自己調用EndInvoke方法 異步執行方法的最簡單方式是以BeginInvoke開始,對主線程執行一些操作,然后調用EndInvoke,EndInvoke直到異步調用完成后才返回
還是先來段自己喜歡的控制台代碼:
- using System;
- namespace ConsoleApplication1
- {
- class Class1
- {
- public delegate void AsyncEventHandler();
- void Event1()
- {
- Console.WriteLine("Event1 Start");
- System.Threading.Thread.Sleep(2000);
- Console.WriteLine("Event1 End");
- }
- void Event2()
- {
- Console.WriteLine("Event2 Start");
- int i=1;
- while(i<1000)
- {
- i=i+1;
- Console.WriteLine("Event2 "+i.ToString());
- }
- Console.WriteLine("Event2 End");
- }
- void CallbackMethod(IAsyncResult ar)
- {
- ((AsyncEventHandler) ar.AsyncState).EndInvoke(ar);
- }
- [STAThread]
- static void Main(string[] args)
- {
- long start=0;
- long end=0;
- Class1 c = new Class1();
- Console.WriteLine("ready");
- start=DateTime.Now.Ticks;
- AsyncEventHandler asy = new AsyncEventHandler(c.Event1);
- IAsyncResult ia=asy.BeginInvoke(null,null);
- c.Event2();
- asy.EndInvoke(ia);
- end =DateTime.Now.Ticks;
- Console.WriteLine("時間刻度差="+ Convert.ToString(end-start) );
- Console.ReadLine();
- }
- }
- }
此程序簡單,異步的處理過程在代碼43-46這幾行 結果如下:
現在讓我們來看看同步處理 修改代碼43-46這幾行代碼: c.Event1(); c.Event2(); 結果如下:
前者的時間刻度大大小於后者 我們可以明顯地看到異步運行的速度優越性
2.方案2-采用查詢(IsCompleted屬性) IAsyncResult.IsCompleted屬性獲取異步操作是否已完成的指示,發現異步調用何時完成. 再次修改代碼43-46這幾行代碼: AsyncEventHandler asy = new AsyncEventHandler(c.Event1); IAsyncResult ia=asy.BeginInvoke(null,null); c.Event2(); while(!ia.IsCompleted) { } asy.EndInvoke(ia);
3.方案3-采用AsyncWaitHandle來等待方法調用的完成 IAsyncResult.AsyncWaitHandle屬性獲取用於等待異步操作完成的WaitHandle WaitHandle.WaitOne方法阻塞當前線程,直到當前的WaitHandle收到信號 使用WaitHandle,則在異步調用完成之后,但在通過調用EndInvoke結果之前,可以執行其他處理 再次修改代碼43-46這幾行代碼: AsyncEventHandler asy = new AsyncEventHandler(c.Event1); IAsyncResult ia=asy.BeginInvoke(null,null); c.Event2(); ia.AsyncWaitHandle.WaitOne();
4.方案4-利用回調函數 如果啟動異步調用的線程不需要處理調用結果,則可以在調用完成時執行回調方法 要使用回調方法,必須將代表該方法的AsyncCallback委托傳遞給BeginInvoke 再次修改代碼43-46這幾行代碼: AsyncEventHandler asy = new AsyncEventHandler(c.Event1); asy.BeginInvoke(new AsyncCallback(c.CallbackMethod),asy); c.Event2();