from:http://www.myext.cn/csharp/a_6765.htmlhtml
也許業內不少高不成低不就的程序員都會對一些知識點會有些迷惑,緣由是日常工做用的少,因此也就決定了你對這個事物的瞭解程度。今天就來看看C#中異步方法的使用。但願對你們有所幫助。程序員
--原文編程
一般狀況下,若是須要異步執行一個耗時的操做,咱們會新起一個線程,而後讓這個線程去執行代碼。可是對於每個異步調用都經過建立線程來進行操做顯 然會對性能產生必定的影響,同時操做也相對繁瑣一些。.Net中能夠經過委託進行方法的異步調用,就是說客戶端在異步調用方法時,自己並不會由於方法的調 用而中斷,而是從線程池中抓取一個線程去執行該方法,自身線程(主線程)在完成抓取線程這一過程以後,繼續執行下面的代碼,這樣就實現了代碼的並行執行。 使用線程池的好處就是避免了頻繁進行異步調用時建立、銷燬線程的開銷。app
如同上面所示,當咱們在委託對象上調用BeginInvoke()時,便進行了一個異步的方法調用。而在這種狀況下使用異步編程時,就須要進行更多 的控制,好比當異步執行方法的方法結束時通知客戶端、返回異步執行方法的返回值等。本節就對BeginInvoke()方法、EndInvoke()方法 和其相關的IAysncResult作一個簡單的介紹。異步
NOTE:討論在客戶端程序中異步地調用方法。async
咱們看這樣一段代碼,它演示了不使用異步調用的一般狀況:異步編程
1 class Program7 { 2 static void Main(string[] args) { 3 4 Console.WriteLine("Client application started! "); 5 Thread.CurrentThread.Name = "Main Thread"; 6 7 Calculator cal = new Calculator(); 8 int result = cal.Add(2, 5); 9 Console.WriteLine("Result: {0} ", result); 10 11 // 作某些其它的事情,模擬須要執行3秒鐘 12 for (int i = 1; i <= 3; i++) { 13 Thread.Sleep(TimeSpan.FromSeconds(1)); 14 Console.WriteLine("{0}: Client executed {1} second(s).", 15 Thread.CurrentThread.Name, i); 16 } 17 18 Console.WriteLine(" Press any key to exit..."); 19 Console.ReadKey(); 20 } 21 } 22 23 public class Calculator { 24 public int Add(int x, int y) { 25 if (Thread.CurrentThread.IsThreadPoolThread) { 26 Thread.CurrentThread.Name = "Pool Thread"; 27 } 28 Console.WriteLine("Method invoked!"); 29 30 // 執行某些事情,模擬須要執行2秒鐘 31 for (int i = 1; i <= 2; i++) { 32 Thread.Sleep(TimeSpan.FromSeconds(1)); 33 Console.WriteLine("{0}: Add executed {1} second(s).", 34 Thread.CurrentThread.Name, i); 35 } 36 Console.WriteLine("Method complete!"); 37 return x + y; 38 } 39 }
上面代碼有幾個關於對於線程的操做,若是不瞭解能夠看一下下面的說明,若是你已經瞭解能夠直接跳過:性能
通 過這幾個方法和屬性,有助於咱們更好地調試異步調用方法。上面代碼中除了加入了一些對線程的操做之外再沒有什麼特別之處。咱們建了一個 Calculator類,它只有一個Add方法,咱們模擬了這個方法須要執行2秒鐘時間,而且每隔一秒進行一次輸出。而在客戶端程序中,咱們使用 result變量保存了方法的返回值並進行了打印。隨後,咱們再次模擬了客戶端程序接下來的操做須要執行2秒鐘時間。運行這段程序,會產生下面的輸出:spa
Client application started!
Method invoked! Main Thread: Add executed 1 second(s). Main Thread: Add executed 2 second(s). Method complete! Result: 7 Main Thread: Client executed 1 second(s). Main Thread: Client executed 2 second(s). Main Thread: Client executed 3 second(s). Press any key to exit...
若是你確實執行了這段代碼,會看到這些輸出並非一瞬間輸出的,而是執行了大概5秒鐘的時間,由於線程是串行執行的,因此在執行完Add()方法以後纔會繼續客戶端剩下的代碼。線程
接下來咱們定義一個AddDelegate委託,並使用BeginInvoke()方法來異步地調用它。在上面已經介紹 過,BeginInvoke()除了 最後兩個參數爲AsyncCallback類型和Object類型之外,前面的參數類型和個數與委託的方法定義相同。另外BeginInvoke()方法 返回了一個實現了IAsyncResult接口的對象(實際上就是一個 AsyncResult(System.Runtime.Remoting.Messaging命名空間裏)類型實例,注意這裏IAsyncResult 和 AysncResult是不一樣的)。
AsyncResult的用途有這麼幾個:傳遞參數,它 包含了對調用了BeginInvoke()的委託的引用;它還包含了BeginInvoke()的最後一個Object類型的參數;它能夠鑑別出是哪一個方 法的哪一次調用,由於經過同一個委託變量能夠對同一個方法調用屢次。
EndInvoke()方法接受IAsyncResult類型的對象 (以及ref和out類型參數,這裏不討論了,對它們的處理和返回值相似),因此在調用BeginInvoke()以後,咱們須要保留 IAsyncResult,以便在調用EndInvoke()時進行傳遞。這裏最重要的就是EndInvoke()方法的返回值,它就是方法的返回值。除 此之外,當客戶端調用EndInvoke()時,若是異步調用的方法沒有執行完畢,則會中斷當前線程而去等待該方法,只有當異步方法執行完畢後纔會繼續執 行後面的代碼。因此在調用完BeginInvoke()後當即執行EndInvoke()是沒有任何意義的。咱們一般在儘量早的時候調用 BeginInvoke(),而後在須要方法的返回值的時候再去調用EndInvoke(),或者是根據狀況在晚些時候調用。說了這麼多,咱們如今看一下 使用異步調用改寫後上面的代碼吧:
1 public delegate int AddDelegate(int x, int y); 2 3 class Program8 { 4 5 static void Main(string[] args) { 6 7 Console.WriteLine("Client application started! "); 8 Thread.CurrentThread.Name = "Main Thread"; 9 10 Calculator cal = new Calculator(); 11 AddDelegate del = new AddDelegate(cal.Add); 12 IAsyncResult asyncResult = del.BeginInvoke(2,5,null,null); // 異步調用方法 13 14 // 作某些其它的事情,模擬須要執行3秒鐘 15 for (int i = 1; i <= 3; i++) { 16 Thread.Sleep(TimeSpan.FromSeconds(i)); 17 Console.WriteLine("{0}: Client executed {1} second(s).", 18 Thread.CurrentThread.Name, i); 19 } 20 21 int rtn = del.EndInvoke(asyncResult); 22 Console.WriteLine("Result: {0} ", rtn); 23 24 Console.WriteLine(" Press any key to exit..."); 25 Console.ReadKey(); 26 } 27 } 28 29 public class Calculator { /* 與上面同,略 */}
此時的輸出爲:
Client application started!
Method invoked! Main Thread: Client executed 1 second(s). Pool Thread: Add executed 1 second(s). Main Thread: Client executed 2 second(s). Pool Thread: Add executed 2 second(s). Method complete! Main Thread: Client executed 3 second(s). Result: 7 Press any key to exit...
如今執行完這段代碼只須要3秒鐘時間,兩個for循環所產生的輸出交替進行,這也說明了這兩段代碼並行執行的狀況。能夠看到Add()方法是由線程 池中的線程在執行,由於Thread.CurrentThread.IsThreadPoolThread返回了True,同時咱們對該線程命名爲了 Pool Thread。另外咱們能夠看到經過EndInvoke()方法獲得了返回值。
有時候,咱們可能會將得到返回值的操做放到另外一段 代碼或者客戶端去執行,而不是向上面那樣直接寫在BeginInvoke()的後面。好比說咱們在Program中新建一個方法GetReturn(), 此時能夠經過AsyncResult的AsyncDelegate得到del委託對象,而後再在其上調用EndInvoke()方法,這也說明了 AsyncResult能夠惟一的獲取到與它相關的調用了的方法(或者也能夠理解成委託對象)。因此上面獲取返回值的代碼也能夠改寫成這樣:
static int GetReturn(IAsyncResult asyncResult) { AsyncResult result = (AsyncResult)asyncResult; AddDelegate del = (AddDelegate)result.AsyncDelegate; int rtn = del.EndInvoke(asyncResult); return rtn; }
而後再將int rtn = del.EndInvoke(asyncResult);語句改成int rtn = GetReturn(asyncResult);。注意上面IAsyncResult要轉換爲實際的類型AsyncResult才能訪問 AsyncDelegate屬性,由於它沒有包含在IAsyncResult接口的定義中。
BeginInvoke的另外兩個參數分別是AsyncCallback和Object類型,其中AsyncCallback是一個委託類型,它用於方法的回調,便是說當異步方法執行完畢時自動進行調用的方法。它的定義爲:
public delegate void AsyncCallback(IAsyncResult ar);
Object類型用於傳遞任何你想要的數值,它能夠經過IAsyncResult的AsyncState屬性得到。下面咱們將獲取方法返回值、打印返回值的操做放到了OnAddComplete()回調方法中:
public delegate int AddDelegate(int x, int y); class Program9 { static void Main(string[] args) { Console.WriteLine("Client application started! "); Thread.CurrentThread.Name = "Main Thread"; Calculator cal = new Calculator(); AddDelegate del = new AddDelegate(cal.Add); string data = "Any data you want to pass."; AsyncCallback callBack = new AsyncCallback(OnAddComplete); del.BeginInvoke(2, 5, callBack, data); // 異步調用方法 // 作某些其它的事情,模擬須要執行3秒鐘 for (int i = 1; i <= 3; i++) { Thread.Sleep(TimeSpan.FromSeconds(i)); Console.WriteLine("{0}: Client executed {1} second(s).", Thread.CurrentThread.Name, i); } Console.WriteLine(" Press any key to exit..."); Console.ReadKey(); } static void OnAddComplete(IAsyncResult asyncResult) { AsyncResult result = (AsyncResult)asyncResult; AddDelegate del = (AddDelegate)result.AsyncDelegate; string data = (string)asyncResult.AsyncState; int rtn = del.EndInvoke(asyncResult); Console.WriteLine("{0}: Result, {1}; Data: {2} ", Thread.CurrentThread.Name, rtn, data); } } public class Calculator { /* 與上面同,略 */}
產生的輸出爲:
Client application started!
Method invoked! Main Thread: Client executed 1 second(s). Pool Thread: Add executed 1 second(s). Main Thread: Client executed 2 second(s). Pool Thread: Add executed 2 second(s). Method complete! Pool Thread: Result, 7; Data: Any data you want to pass. Main Thread: Client executed 3 second(s). Press any key to exit...
這裏有幾個值得注意的地方:一、咱們在調用BeginInvoke()後再也不須要保存IAysncResult了,由於AysncCallback 委託將該對象定義在了回調方法的參數列表中;二、咱們在OnAddComplete()方法中得到了調用BeginInvoke()時最後一個參數傳遞的 值,字 符串「Any data you want to pass」;三、執行回調方法的線程並不是客戶端線程Main Thread,而是來自線程池中的線程Pool Thread。另外如前面所說,在調用EndInvoke()時有可能會拋出異常,因此在應該將它放到try/catch塊中,這裏我就再也不示範了。
結語:這篇文章簡單的描述了異步委託方法的使用和說明,本文是從原做者內容中摘抄而來,作了不多的改動,望見諒。
感謝閱讀,但願對你能有所幫助。
原文地址:http://www.cnblogs.com/JimmyZhang/archive/2008/08/22/1274342.html