[深入學習C#]完成異步委托的三種方式


簡介

  創建線程的一個簡單方式是定義一個委托,並且異步調用它。委托是方法的類型安全的引用。Delegate類還支持異步地調用方法。在后台,Delegate類會創建一個執行任務的線程。 
  參考文獻

線程

  線程是程序中獨立的指令流,線程對客戶端和服務器端應用程序都非常重要,線程是運行程序所必須的。 
  1.進程包含資源,如Window句柄、文件系統句柄或其他內核對象,每個進程都分配了虛擬內存。 
  2.每個進程至少包含一個線程,操作系統會調度線程。 
  3.線程有一個優先級、實際上正在處理的程序的位置計數器、一個儲存其局部變量的棧。 
  4.每個線程都有自己的棧,但程序代碼的內存和堆由一個進程的所有線程共享。 
  5.同一個進程內的線程間通信非常快,因為它們都尋址相同的虛擬內存。 
  6.同一個進程內的線程可以修改同一個儲存位置。

異步委托

  創建線程的一種簡單方式是定義一個委托,並且異步調用它。在后台,Delegate類會創建一個執行任務的線程。 
  現在我們可以使用不同的技術來異步地調用委托,並且返回結果。 
  在此之前,我們定義一個名為TakesAWhile()方法,該方法中調用了Thread.Sleep()方法:

static int TakesAWhile(int data , int ms) { Console.WriteLine("TakesAWhile started!"); Thread.Sleep(ms); Console.WriteLine("TakesAWhile completed!"); return ++data; }

 

 

  然后定義一個與之有相同參數簽名和返回類型的委托:

public delegate int TakesAWhileDelegate(int data , int ms );

 

 

方法一:使用輪詢

  Delegate類提供了BeginInvoke()方法,在該方法中,可 以傳遞用委托類型定義的輸入參數。 BeginInvoke()方法總是有 AsyncCallback和object 類型的兩個額外參數。現在重 的是 BeginInvoke()方法的返回類型 :IAsyncResult。 通過 IAsyncResult,可 以獲得該委托的相關信息 ,並驗證該委托是否完成了任務,這是IsCompleted屬性的功勞。 只要委托沒有完成其任務,程序的主線程就繼續執行 While循環。

static void Main() { //synchronous method call //TakesAWhile(1 , 3000); //asynchronous by using a delegate TakesAWhileDelegate d1 = TakesAWhile; IAsyncResult ar = d1.BeginInvoke(1, 3000, null ,null); while(!ar.IsCompleted) { //doing something else in the main thread Console.Write("."); Thread.Sleep(50); } int result = d1.EndInvoke(ar); Console.WriteLine("Result:{0}",result); }

 

  運行應用程序的時候,可以看到主線程和委托線程同時運行,在委托線程執行完畢之后,主線程就停止循環。 
  .TakesAWhile started! 
  ..TakesAWhile completed! 
  result: 2 
  除了檢查委托是否完成之外,還可以在完成了由主線程執行的任務之后,調用委托類型的EndInvoke()方法。EndInvoke()方法會一直等待,知道委托完成其任務為止。 
  如果在委托結束前不等待委托完成其任務,主線程就結束,委托線程就會終止。

方法二:使用等待句柄(WaitHandle)

  使用AsyncWaitHandle屬性可以訪問等待句柄。這個屬性返回一個WaitHandle類型的對象,它可以等待委托線程完成任務。WaitOne()方法將一個超時時間作為可選的第一個參數,在其中可以定義要等待的最長時間。如果發生超時,就會返回false。

static void Main() { TakesAWhileDelegate d1 = TakesAWhile; IAsyncResult ar = d1.BeginInvoke(1, 3000, null, null); while(true) { Console.Write("."); if(ar.AsyncWaitHandle.WaitOne(50, false)) { Console.WriteLine("Can get the result now"); break; } } int result = d1.EndInvoke(ar); Console.WriteLine("result:{0}",result); }

 

 

方法三:使用異步回調

  等待委托結構的第三種方式是使用異步回調。在BeginInvoke()方法的第3個參數中,可以傳遞一個滿足AsyncCallBack委托的需求的方法。AsyncCallBack委托定義了一個IAsyncResult類型的參數,其返回類型為void。 
  在這里,我們定義一個名為TakesAWhileCompleted()的方法,它滿足AsyncCallBack的需求,將其作為BeginInvoke()方法的第三個參數。 
  對於BeginInvoke方法的第四個參數,可以傳遞任意對象,以便從回調方法中方為使用它,通過IAsyncResult的AsyncState屬性來訪問它。 
  

static void Main() { TakesAWhileDelegate d1 =TakesAWhile; d1.BeginInvoke(1, 3000 , TakesAWhileCompleted, d1); for(int i = 0 ; i < 100 ; i++) { Console.Write("."); Thread.Sleep(50); } }

  然后我們定義TakesAWhileCompleted()方法,它用AsyncCallBack委托指定的參數可返回類型來定義。如上所說,可以用ar.AsyncState來讀取BeginInvoke()方法傳遞的最后一個參數: 
  

static void TakesAWhileCompleted(IAsyncResult ar) { if( ar == null) throw new ArgumentNullException("ar"); TakesAWhileDelegate d1 = ar.AsyncState as TakesAWhileDelegate; Trace.Assert(d1 != null, "Invalid object type"); int result = d1.EndInvoke(ar); Console.WriteLine("result:{0}",result); }

 

 

  正如我們在前文中說到,可以使用委托的地方,就可以使用Lambda表達式,同樣,這里我們也可以使用Lambda表達式來簡化異步回調。 
  

static void Main() { TakesAWhileDelegate d1 = TakesAWhile; d1.BeginInvoke(1, 3000, ar => { int result = d1.EndInvoke(); Console.WriteLine("result:{0}",result); }), null); for(int i = 0; i < 100; i++) { Console.Write("."); ThreadSleep(50); } }

 

 

  這里我們注意到,在調用BeginInvoke()方法的時候,並沒有為第四個參數賦值,因為Lambda表達式在這里可以訪問外部的變量d1。Lambda表達式的實現代碼仍然是從委托線程中調用,只不過以這種方式定義的時候,不是很明顯。

總結

  編程模型和所有這些包含異步委托的選項——輪詢、等待句柄、異步回調——不僅僅能用於委托,相同的編程模型(異步模式)在.NET Framework的各個地方都能見到。例如,可以使用HttpWebRequest類的BeginGetResponse()方法異步發送HTTP Web請求,使用SqlCommand類的BeginExecuteReader()方法給數據庫發送異步請求。這些類似於委托的BeginInvoke()方法的參數,也可以使用相同的方式獲得。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM