淺析C#異步操作
.NET Framework 為異步操作提供了兩種設計模式:使用 IAsyncResult 對象的異步操作與使用事件的異步操作。先來學習前者
概述
IAsyncResult 異步設計模式通過名為 BeginOperationName 和 EndOperationName 的兩個方法來實現(xiàn)原同步方法的異步調用,如 FileStream 類提供了 BeginRead 和 EndRead 方法來從文件異步讀取字節(jié),它們是 Read 方法的異步版本
Begin 方法包含同步方法簽名中的任何參數,此外還包含另外兩個參數:一個AsyncCallback 委托和一個用戶定義的狀態(tài)對象。委托用來調用回調方法,狀態(tài)對象是用來向回調方法傳遞狀態(tài)信息。該方法返回一個實現(xiàn) IAsyncResult 接口的對象
End 方法用于結束C#異步操作并返回結果,因此包含同步方法簽名中的 ref 和 out 參數,返回值類型也與同步方法相同。該方法還包括一個 IAsyncResult 參數,用于獲取異步操作是否完成的信息,當然在使用時就必須傳入對應的 Begin 方法返回的對象實例
開始C#異步操作后如果要阻止應用程序,可以直接調用 End 方法,這會阻止應用程序直到異步操作完成后再繼續(xù)執(zhí)行。也可以使用 IAsyncResult 的 AsyncWaitHandle 屬性,調用其中的WaitOne等方法來阻塞線程。這兩種方法的區(qū)別不大,只是前者必須一直等待而后者可以設置等待超時
如果不阻止應用程序,則可以通過輪循 IAsyncResult 的 IsCompleted 狀態(tài)來判斷操作是否完成,或使用 AsyncCallback 委托來結束C#異步操作。AsyncCallback 委托包含一個 IAsyncResult 的簽名,回調方法內部再調用 End 方法來獲取操作執(zhí)行結果
嘗試
先來熟悉一下今天的主角,IAsyncResult 接口
- public interface IAsyncResult
- {
- object AsyncState { get; }
- WaitHandle AsyncWaitHandle { get; }
- bool CompletedSynchronously { get; }
- bool IsCompleted { get; }
- }
我用一個 AsyncDemo 類作為異步方法的提供者,后面的程序都會調用它。內部很簡單,構造函數接收一個字符串作為 name ,Run 方法輸出 "My name is " + name ,而異步方法直接用委托的 BeginInvoke 和 EndInvoke 方法實現(xiàn)。
- public class AsyncDemo
- {
- // Use in asynchronous methods
- private delegate string runDelegate();
- private string m_Name;
- private runDelegate m_Delegate;
- public AsyncDemo(string name)
- {
- m_Name = name;
- m_Delegate = new runDelegate(Run);
- }
- /**////
- /// Synchronous method
- ///
- ///
- public string Run()
- {
- return "My name is " + m_Name;
- }
- /**////
- /// Asynchronous begin method
- ///
- ///
- ///
- ///
- public IAsyncResult BeginRun(AsyncCallback callBack, Object stateObject)
- {
- try
- {
- return m_Delegate.BeginInvoke(callBack, stateObject);
- }
- catch(Exception e)
- {
- // Hide inside method invoking stack
- throw e;
- }
- }
- /**////
- /// Asynchronous end method
- ///
- ///
- ///
- public string EndRun(IAsyncResult ar)
- {
- if (ar == null)
- throw new NullReferenceException("Arggument ar can't be null");
- try
- {
- return m_Delegate.EndInvoke(ar);
- }
- catch (Exception e)
- {
- // Hide inside method invoking stack
- throw e;
- }
- }
- }
首先是 Begin 之后直接調用 End 方法,當然中間也可以做其他的操作。
- class AsyncTest
- {
- static void Main(string[] args)
- {
- AsyncDemo demo = new AsyncDemo("jiangnii");
- // Execute begin method
- IAsyncResult ar = demo.BeginRun(null, null);
- // You can do other things here
- // Use end method to block thread until the operation is complete
- string demodemoName = demo.EndRun(ar);
- Console.WriteLine(demoName);
- }
- }
也可以用 IAsyncResult 的 AsyncWaitHandle 屬性,我在這里設置為1秒超時。
- class AsyncTest
- {
- static void Main(string[] args)
- {
- AsyncDemo demo = new AsyncDemo("jiangnii");
- // Execute begin method
- IAsyncResult ar = demo.BeginRun(null, null);
- // You can do other things here
- // Use AsyncWaitHandle.WaitOne method to block thread for 1 second at most
- ar.AsyncWaitHandle.WaitOne(1000, false);
- if (ar.IsCompleted)
- {
- // Still need use end method to get result,
- // but this time it will return immediately
- string demodemoName = demo.EndRun(ar);
- Console.WriteLine(demoName);
- }
- else
- {
- Console.WriteLine("Sorry, can't get demoName, the time is over");
- }
- }
- }
不中斷的輪循,每次循環(huán)輸出一個 "."
- class AsyncTest
- {
- static void Main(string[] args)
- {
- AsyncDemo demo = new AsyncDemo("jiangnii");
- // Execute begin method
- IAsyncResult ar = demo.BeginRun(null, null);
- Console.Write("Waiting..");
- while (!ar.IsCompleted)
- {
- Console.Write(".");
- // You can do other things here
- }
- Console.WriteLine();
- // Still need use end method to get result,
- // but this time it will return immediately
- string demodemoName = demo.EndRun(ar);
- Console.WriteLine(demoName);
- }
- }
***是使用回調方法并加上狀態(tài)對象,狀態(tài)對象被作為 IAsyncResult 參數的 AsyncState 屬性被傳給回調方法?;卣{方法執(zhí)行前不能讓主線程退出,我這里只是簡單的讓其休眠了1秒。另一個與之前不同的地方是 AsyncDemo 對象被定義成了類的靜態(tài)字段,以便回調方法使用
- class AsyncTest
- {
- static AsyncDemo demo = new AsyncDemo("jiangnii");
- static void Main(string[] args)
- {
- // State object
- bool state = false;
- // Execute begin method
- IAsyncResult ar = demo.BeginRun(new AsyncCallback(outPut), state);
- // You can do other thins here
- // Wait until callback finished
- System.Threading.Thread.Sleep(1000);
- }
- // Callback method
- static void outPut(IAsyncResult ar)
- {
- bool state = (bool)ar.AsyncState;
- string demodemoName = demo.EndRun(ar);
- if (state)
- {
- Console.WriteLine(demoName);
- }
- else
- {
- Console.WriteLine(demoName + ", isn't it?");
- }
- }
- }
C#異步操作總結
對于一個已經實現(xiàn)了 BeginOperationName 和 EndOperationName 方法的對象,我們可以直接用上述方式調用,但對于只有同步方法的對象,我們要對其進行異步調用也不需要增加對應的異步方法,而只需定義一個委托并使用其 BeginInvoke 和 EndInvoke 方法就可以了
【編輯推薦】