C#線程委托BeginInvoke與EndInvoke的用法
我們已經知道 C#當中 存在async/await 、BackGroudWorker類以及TPL(任務並行庫)。當然C#還有一些舊的模式來支持異步編程。
1. BeginInovke和EndInvoke簡單介紹
delegate long MyDel(int first, int second); class Program { static long Sum(int x, int y) { Console.WriteLine("------Inside Sum@{0}", DateTime.Now.ToString()); Thread.Sleep(2000); return x + y; } static void Main(string[] args) { MyDel del = new MyDel(Sum); Console.WriteLine("Before BeginInvoke---@{0}", DateTime.Now.ToString()); IAsyncResult iar = del.BeginInvoke(3, 5, null, null); Console.WriteLine("After BeginInvoke@{0}", DateTime.Now.ToString()); Console.WriteLine("Doing stuff@{0}", DateTime.Now.ToString()); long result = del.EndInvoke(iar); Console.WriteLine("End Invoke@{0}", DateTime.Now.ToString()); Console.WriteLine("After EndInvoke: {0}", result); Console.ReadKey(); } }
如上代碼,定義瞭一個委托 MyDel ,並且在調用的時候把Sum方法傳給瞭它的對象。一般情況下我們調用這個委托對象,它就會調用他調用列表中包含的方法。就想調用方法一樣,這是同步完成的。
但是如果委托對象在調用列表中隻有一個方法(引用方法),它就可以異步的去執行這個方法。BeginInovke和EndInvoke就是用來做這個事的。我們可以用如下的方式使用:
- ①當我們調用BeginInvoke方法的時候,他開始在一個獨立的線程上執行引用方法,並且立即返回到原始線程。原始線程可以繼續,而引用方法會在想吃的線程中並行執行。
- ②當程序希望獲取已完成的異步方法的結果時,可以檢查BeginInvoke返回的IAsyncResult的IsCompleted屬性,或者調用委托的EndInvoke方法來等待委托執行完成。
上面的使用過程就引出的三種模式:
- ①等待-直到完成 原始線程在發起瞭異步方法以及做瞭一些其他處理之後,原始線程就中斷並且等待異步方法執行完成之後再繼續。
- ②輪詢 ,原始線程定期檢查發起的線程是否完成,如果沒有則可以繼續做其他的事情,
- ③回調 原始線程一直執行,無需等待或者檢查發起的線程是否完成,發起的線程中的引用發放完成之後,發起的線程會調用回調方法,由回調方法在調用的EndInvoke之前處理異步方法的結果。
2.BeginInovke和EndInvoke詳細
對於 BeginInvoke 方法,有幾個註意的地方
① 我們可以根據上面的代碼知道,BeginInvoke的參數包含如下兩個部分
- 引用方法的參數
- CallBack參數和State參數
②BeginInvoke 會在線程池中找到一個線程,讓引用方法運行在該線程上
③BeginInvoke 返回給調用線程一個實現IAsyncResult接口的對象的引用。這個接口引用包含瞭在線程池線程中運行的異步方法的狀態。可以判斷這個狀態來確定異步方法是否結束。
// 3和5是引用方法參數,兩個null分別是Callback參數和State參數 // iar是新線程的信息 IAsyncResult iar = del.BeginInvoke(3, 5, null, null);
對於 EndInvoke 方法,有幾個註意的地方
①他的參數是上面BegionInvoke返回的IAsyncResult接口的引用對象,傳入這個對象是便於EndInvoke去找到引用方法運行的線程。並且這個參數置於參數列表最後一個。EndInvoke提供瞭從異步方法調用的所有輸出,包括ref和out參數。如果委托的引用方法有ref和out參數,他們必須包含在EndInvoke的參數列表當中
IAsyncResult iar2 = del2.BeginInvoke(3, 5, out res, null, null); del2.EndInvoke(out res, iar2);
②如果線程已經退出瞭,EndInvoke會做如下事情:
- 清理退出的線程的狀態並且釋放資源
- 找到引用方法的返回值,並作為自己的返回值
③如果EndInovke發現線程還在運行中,那麼調用線程就會停止並等待,直到清理完畢並返回值。
④因為EndInvoke會去清理線程信息,所以BeginInvoke和EndInvoke必須成對使用。
⑤如果異步方法出現異常,那麼在調用EndInvoke的時候會拋出異常。
3.AsyncResult類
上面說BeginInvoke方法返回瞭一個IAsyncResult接口的引用對象(內部是AsyncResult類的對象),AsyncResult類型表現瞭異步方法的狀態。下面是這類的主要組成部分:
4.三種模式
① 等待-直到完成 (比較簡單的模式)
//開始執行異步方法 IAsyncResult iar = del.BeginInvoke(3, 5, null, null); //Do Something 耗時 del2.EndInvoke(iar);
像上面的代碼,BeginInvoke之後,做瞭一些事情,然後調用EndInvoke來處理結果,這種方式就是等待-直到完成的模式。
②輪詢模式
輪詢模式中,原始的線程發起瞭異步的方法調用,做一些事情,然後使用IAsyncResult中的IsComplete熟悉來定期檢查開啟的線程是否完成。如果未完成就在去做一些其他事情。
delegate long MyDel(int first, int second); class Program { static long Sum(int x, int y) { Console.WriteLine("--Inside Sum@{0}", DateTime.Now.ToString()); Thread.Sleep(200); return x + y; } static void Main(string[] args) { MyDel del = new MyDel(Sum); //開始執行異步方法 IAsyncResult iar = del.BeginInvoke(3, 5, null, null); //輪詢開始 while (!iar.IsCompleted) { //未完成,執行下面的語句 for (long i = 0; i < 20000000; i++) ; } //執行完成調用EndInvoke獲取結果 long result = del.EndInvoke(iar); Console.ReadKey(); } }
③回調模式
前兩種都是主動方式的,原始線程一直在監控這新開啟的線程。但是回調是被動的,一旦原始線程發起瞭異步方法,它就自己管自己瞭,不在考慮同步。
當異步方法調用結束之後,系統調用一個用戶自定義的方法來處理結果,並且調用委托的EndInvoke方法。這個用戶自定義的方法就是回調方法。
上面的BegionInvoke中寫過,他會有兩個參數一個Callback參數和一個State參數.
CallBack參數:是回調方法的名稱。
State參數:可以是null,或者傳入回調方法的一個對象的引用。我們可以用IAsyncResult參數的AsyncState屬性來獲取這個對象,參數的類型是object。
a.回調方法:
回調方法的簽名和返回類型必須和 AsyncCallback委托類型所描述的形式一致。
兩種方式,構建這個AsyncCallback參數
new AsyncCallback 對象
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone),null);
直接傳回調方法的名稱
IAsyncResult iar = del.BeginInvoke(3, 5, CallWhenDone, null);
其中 CallWhenDone 如下:
static void CallWhenDone(IAsyncResult iar) { AsyncResult ar = (AsyncResult)iar; MyDel del = (MyDel)ar.AsyncDelegate; //回調方法中調用瞭EndInvoke long result = del.EndInvoke(iar); }
b.在回調方法中調用EndInvoke
上面代碼中,在回調中使用瞭EndInvoke,上文中說到 EndInvoke的調用,是委托的調用,並且需要傳入一個IAsyncResult的接口對象的引用。
所以想要在回調方法裡面,調用這個EndInvoke,就得拿到兩個東西一個是委托對象、一個是IAsyncResult,由於我們AsyncCallback委托本身就是必須要傳入IAsyncResult 的,所以這個比較容易,剩下的就是委托對象本身瞭。在AsyncResult類小節裡面我看到,它裡面存著一個 AsyncDelegate(它就是委托對象的引用),還有就是 IAsyncResult接口對象在內部就是AsyncResult類對象。所以才可以像上main的代碼,通過強制類型轉換得到MyDel的對象。
第二種方法就是如果State參數沒有用處,可以通過State參數,把委托的對象傳過去。
調用的地方,最後一個參數傳入del
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone),del);
回調方法:
static void CallWhenDone(IAsyncResult iar) { MyDel del = (MyDel)iar.AsyncState; long result = del.EndInvoke(iar); }
以上就是這篇文章的全部內容瞭,希望本文的內容對大傢的學習或者工作具有一定的參考學習價值,謝謝大傢對WalkonNet的支持。