淺析C#異步操作
.NET Framework 為異步操作提供了兩種設(shè)計(jì)模式:使用 IAsyncResult 對象的異步操作與使用事件的異步操作。先來學(xué)習(xí)前者
概述
IAsyncResult 異步設(shè)計(jì)模式通過名為 BeginOperationName 和 EndOperationName 的兩個(gè)方法來實(shí)現(xiàn)原同步方法的異步調(diào)用,如 FileStream 類提供了 BeginRead 和 EndRead 方法來從文件異步讀取字節(jié),它們是 Read 方法的異步版本
Begin 方法包含同步方法簽名中的任何參數(shù),此外還包含另外兩個(gè)參數(shù):一個(gè)AsyncCallback 委托和一個(gè)用戶定義的狀態(tài)對象。委托用來調(diào)用回調(diào)方法,狀態(tài)對象是用來向回調(diào)方法傳遞狀態(tài)信息。該方法返回一個(gè)實(shí)現(xiàn) IAsyncResult 接口的對象
End 方法用于結(jié)束C#異步操作并返回結(jié)果,因此包含同步方法簽名中的 ref 和 out 參數(shù),返回值類型也與同步方法相同。該方法還包括一個(gè) IAsyncResult 參數(shù),用于獲取異步操作是否完成的信息,當(dāng)然在使用時(shí)就必須傳入對應(yīng)的 Begin 方法返回的對象實(shí)例
開始C#異步操作后如果要阻止應(yīng)用程序,可以直接調(diào)用 End 方法,這會阻止應(yīng)用程序直到異步操作完成后再繼續(xù)執(zhí)行。也可以使用 IAsyncResult 的 AsyncWaitHandle 屬性,調(diào)用其中的WaitOne等方法來阻塞線程。這兩種方法的區(qū)別不大,只是前者必須一直等待而后者可以設(shè)置等待超時(shí)
如果不阻止應(yīng)用程序,則可以通過輪循 IAsyncResult 的 IsCompleted 狀態(tài)來判斷操作是否完成,或使用 AsyncCallback 委托來結(jié)束C#異步操作。AsyncCallback 委托包含一個(gè) IAsyncResult 的簽名,回調(diào)方法內(nèi)部再調(diào)用 End 方法來獲取操作執(zhí)行結(jié)果
嘗試
先來熟悉一下今天的主角,IAsyncResult 接口
- public interface IAsyncResult
 - {
 - object AsyncState { get; }
 - WaitHandle AsyncWaitHandle { get; }
 - bool CompletedSynchronously { get; }
 - bool IsCompleted { get; }
 - }
 
我用一個(gè) AsyncDemo 類作為異步方法的提供者,后面的程序都會調(diào)用它。內(nèi)部很簡單,構(gòu)造函數(shù)接收一個(gè)字符串作為 name ,Run 方法輸出 "My name is " + name ,而異步方法直接用委托的 BeginInvoke 和 EndInvoke 方法實(shí)現(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 之后直接調(diào)用 End 方法,當(dāng)然中間也可以做其他的操作。
- 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 屬性,我在這里設(shè)置為1秒超時(shí)。
- 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)輸出一個(gè) "."
- 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);
 - }
 - }
 
***是使用回調(diào)方法并加上狀態(tài)對象,狀態(tài)對象被作為 IAsyncResult 參數(shù)的 AsyncState 屬性被傳給回調(diào)方法?;卣{(diào)方法執(zhí)行前不能讓主線程退出,我這里只是簡單的讓其休眠了1秒。另一個(gè)與之前不同的地方是 AsyncDemo 對象被定義成了類的靜態(tài)字段,以便回調(diào)方法使用
- 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#異步操作總結(jié)
對于一個(gè)已經(jīng)實(shí)現(xiàn)了 BeginOperationName 和 EndOperationName 方法的對象,我們可以直接用上述方式調(diào)用,但對于只有同步方法的對象,我們要對其進(jìn)行異步調(diào)用也不需要增加對應(yīng)的異步方法,而只需定義一個(gè)委托并使用其 BeginInvoke 和 EndInvoke 方法就可以了
【編輯推薦】















 
 
 
 
 
 
 