C#線程委托BeginInvoke與EndInvoke的用法
我們已經(jīng)知道 C#當(dāng)中 存在async/await 、BackGroudWorker類以及TPL(任務(wù)并行庫)。當(dāng)然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(); } }
如上代碼,定義了一個(gè)委托 MyDel ,并且在調(diào)用的時(shí)候把Sum方法傳給了它的對象。一般情況下我們調(diào)用這個(gè)委托對象,它就會(huì)調(diào)用他調(diào)用列表中包含的方法。就想調(diào)用方法一樣,這是同步完成的。
但是如果委托對象在調(diào)用列表中只有一個(gè)方法(引用方法),它就可以異步的去執(zhí)行這個(gè)方法。BeginInovke和EndInvoke就是用來做這個(gè)事的。我們可以用如下的方式使用:
- ①當(dāng)我們調(diào)用BeginInvoke方法的時(shí)候,他開始在一個(gè)獨(dú)立的線程上執(zhí)行引用方法,并且立即返回到原始線程。原始線程可以繼續(xù),而引用方法會(huì)在想吃的線程中并行執(zhí)行。
- ②當(dāng)程序希望獲取已完成的異步方法的結(jié)果時(shí),可以檢查BeginInvoke返回的IAsyncResult的IsCompleted屬性,或者調(diào)用委托的EndInvoke方法來等待委托執(zhí)行完成。
上面的使用過程就引出的三種模式:
- ①等待-直到完成 原始線程在發(fā)起了異步方法以及做了一些其他處理之后,原始線程就中斷并且等待異步方法執(zhí)行完成之后再繼續(xù)。
- ②輪詢 ,原始線程定期檢查發(fā)起的線程是否完成,如果沒有則可以繼續(xù)做其他的事情,
- ③回調(diào) 原始線程一直執(zhí)行,無需等待或者檢查發(fā)起的線程是否完成,發(fā)起的線程中的引用發(fā)放完成之后,發(fā)起的線程會(huì)調(diào)用回調(diào)方法,由回調(diào)方法在調(diào)用的EndInvoke之前處理異步方法的結(jié)果。
2.BeginInovke和EndInvoke詳細(xì)
對于 BeginInvoke 方法,有幾個(gè)注意的地方
① 我們可以根據(jù)上面的代碼知道,BeginInvoke的參數(shù)包含如下兩個(gè)部分
- 引用方法的參數(shù)
- CallBack參數(shù)和State參數(shù)
②BeginInvoke 會(huì)在線程池中找到一個(gè)線程,讓引用方法運(yùn)行在該線程上
③BeginInvoke 返回給調(diào)用線程一個(gè)實(shí)現(xiàn)IAsyncResult接口的對象的引用。這個(gè)接口引用包含了在線程池線程中運(yùn)行的異步方法的狀態(tài)??梢耘袛噙@個(gè)狀態(tài)來確定異步方法是否結(jié)束。
// 3和5是引用方法參數(shù),兩個(gè)null分別是Callback參數(shù)和State參數(shù) // iar是新線程的信息 IAsyncResult iar = del.BeginInvoke(3, 5, null, null);
對于 EndInvoke 方法,有幾個(gè)注意的地方
①他的參數(shù)是上面BegionInvoke返回的IAsyncResult接口的引用對象,傳入這個(gè)對象是便于EndInvoke去找到引用方法運(yùn)行的線程。并且這個(gè)參數(shù)置于參數(shù)列表最后一個(gè)。EndInvoke提供了從異步方法調(diào)用的所有輸出,包括ref和out參數(shù)。如果委托的引用方法有ref和out參數(shù),他們必須包含在EndInvoke的參數(shù)列表當(dāng)中
IAsyncResult iar2 = del2.BeginInvoke(3, 5, out res, null, null); del2.EndInvoke(out res, iar2);
②如果線程已經(jīng)退出了,EndInvoke會(huì)做如下事情:
- 清理退出的線程的狀態(tài)并且釋放資源
- 找到引用方法的返回值,并作為自己的返回值
③如果EndInovke發(fā)現(xiàn)線程還在運(yùn)行中,那么調(diào)用線程就會(huì)停止并等待,直到清理完畢并返回值。
④因?yàn)镋ndInvoke會(huì)去清理線程信息,所以BeginInvoke和EndInvoke必須成對使用。
⑤如果異步方法出現(xiàn)異常,那么在調(diào)用EndInvoke的時(shí)候會(huì)拋出異常。
3.AsyncResult類
上面說BeginInvoke方法返回了一個(gè)IAsyncResult接口的引用對象(內(nèi)部是AsyncResult類的對象),AsyncResult類型表現(xiàn)了異步方法的狀態(tài)。下面是這類的主要組成部分:
4.三種模式
① 等待-直到完成 (比較簡單的模式)
//開始執(zhí)行異步方法 IAsyncResult iar = del.BeginInvoke(3, 5, null, null); //Do Something 耗時(shí) del2.EndInvoke(iar);
像上面的代碼,BeginInvoke之后,做了一些事情,然后調(diào)用EndInvoke來處理結(jié)果,這種方式就是等待-直到完成的模式。
②輪詢模式
輪詢模式中,原始的線程發(fā)起了異步的方法調(diào)用,做一些事情,然后使用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); //開始執(zhí)行異步方法 IAsyncResult iar = del.BeginInvoke(3, 5, null, null); //輪詢開始 while (!iar.IsCompleted) { //未完成,執(zhí)行下面的語句 for (long i = 0; i < 20000000; i++) ; } //執(zhí)行完成調(diào)用EndInvoke獲取結(jié)果 long result = del.EndInvoke(iar); Console.ReadKey(); } }
③回調(diào)模式
前兩種都是主動(dòng)方式的,原始線程一直在監(jiān)控這新開啟的線程。但是回調(diào)是被動(dòng)的,一旦原始線程發(fā)起了異步方法,它就自己管自己了,不在考慮同步。
當(dāng)異步方法調(diào)用結(jié)束之后,系統(tǒng)調(diào)用一個(gè)用戶自定義的方法來處理結(jié)果,并且調(diào)用委托的EndInvoke方法。這個(gè)用戶自定義的方法就是回調(diào)方法。
上面的BegionInvoke中寫過,他會(huì)有兩個(gè)參數(shù)一個(gè)Callback參數(shù)和一個(gè)State參數(shù).
CallBack參數(shù):是回調(diào)方法的名稱。
State參數(shù):可以是null,或者傳入回調(diào)方法的一個(gè)對象的引用。我們可以用IAsyncResult參數(shù)的AsyncState屬性來獲取這個(gè)對象,參數(shù)的類型是object。
a.回調(diào)方法:
回調(diào)方法的簽名和返回類型必須和 AsyncCallback委托類型所描述的形式一致。
兩種方式,構(gòu)建這個(gè)AsyncCallback參數(shù)
new AsyncCallback 對象
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone),null);
直接傳回調(diào)方法的名稱
IAsyncResult iar = del.BeginInvoke(3, 5, CallWhenDone, null);
其中 CallWhenDone 如下:
static void CallWhenDone(IAsyncResult iar) { AsyncResult ar = (AsyncResult)iar; MyDel del = (MyDel)ar.AsyncDelegate; //回調(diào)方法中調(diào)用了EndInvoke long result = del.EndInvoke(iar); }
b.在回調(diào)方法中調(diào)用EndInvoke
上面代碼中,在回調(diào)中使用了EndInvoke,上文中說到 EndInvoke的調(diào)用,是委托的調(diào)用,并且需要傳入一個(gè)IAsyncResult的接口對象的引用。
所以想要在回調(diào)方法里面,調(diào)用這個(gè)EndInvoke,就得拿到兩個(gè)東西一個(gè)是委托對象、一個(gè)是IAsyncResult,由于我們AsyncCallback委托本身就是必須要傳入IAsyncResult 的,所以這個(gè)比較容易,剩下的就是委托對象本身了。在AsyncResult類小節(jié)里面我看到,它里面存著一個(gè) AsyncDelegate(它就是委托對象的引用),還有就是 IAsyncResult接口對象在內(nèi)部就是AsyncResult類對象。所以才可以像上main的代碼,通過強(qiáng)制類型轉(zhuǎn)換得到MyDel的對象。
第二種方法就是如果State參數(shù)沒有用處,可以通過State參數(shù),把委托的對象傳過去。
調(diào)用的地方,最后一個(gè)參數(shù)傳入del
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone),del);
回調(diào)方法:
static void CallWhenDone(IAsyncResult iar) { MyDel del = (MyDel)iar.AsyncState; long result = del.EndInvoke(iar); }
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對腳本之家的支持。
相關(guān)文章
C#基礎(chǔ)知識(shí)系列八const和readonly關(guān)鍵字詳細(xì)介紹
這篇文章主要介紹了C#中的const和readonly關(guān)鍵字,有需要的朋友可以參考一下2014-01-01DevExpress實(shí)現(xiàn)為TextEdit設(shè)置水印文字的方法
這篇文章主要介紹了DevExpress實(shí)現(xiàn)為TextEdit設(shè)置水印文字的方法,對C#程序設(shè)計(jì)人員來說是一個(gè)很實(shí)用的技巧,需要的朋友可以參考下2014-08-08C#實(shí)現(xiàn)Base64編碼與解碼及規(guī)則
這篇文章主要介紹了C#實(shí)現(xiàn)Base64編碼與解碼,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-08-08