C# 委托與異步調(diào)用

背景:在WinForm UI中,有時(shí)需要對(duì)控件進(jìn)行比較頻繁的刷新,如進(jìn)度條、PictureBox顯示視頻等。如果在主線程進(jìn)行這些刷新操作,操作還未完成就將執(zhí)行下一次刷新,程序?qū)l(fā)生錯(cuò)誤;如果只是創(chuàng)建另一個(gè)線程執(zhí)行這些操作,將和主線程產(chǎn)生競(jìng)爭(zhēng),造成界面鎖死(因此Windows?GUI編程有一個(gè)規(guī)則,就是只能通過(guò)創(chuàng)建控件的線程來(lái)操作控件的數(shù)據(jù),否則就可能產(chǎn)生不可預(yù)料的結(jié)果)。這時(shí)候,我們就可以用委托與異步來(lái)解決這個(gè)問(wèn)題。

異步:Windows窗體控件,唯一可以從創(chuàng)建它的線程之外的線程中調(diào)用的是Invoke()、BeginInvoke()、EndInvoke()方法和 InvokeRequired 屬性。這些方法會(huì)切換到創(chuàng)建控件的線程上,以調(diào)用賦予一個(gè)委托參數(shù)的方法,該委托參數(shù)可以傳遞給這些方法。

關(guān)于Invoke()與BeginInvoke():其中BeginInvoke()、EndInvoke()方法是Invoke()方法的異步版本。

相同點(diǎn):都需要一個(gè)委托對(duì)象作為參數(shù)。

不同點(diǎn):Invoke()是同步方法,Invoke封送的方法被執(zhí)行完畢前,Invoke()不會(huì)返回,從而調(diào)用者線程將被阻塞;需要等待UI操作執(zhí)行完畢后繼續(xù)執(zhí)行線程時(shí)考慮使用。而B(niǎo)eginInvoke()相反,是異步方法,方法調(diào)后立即返回,不用等待委托方法的執(zhí)行結(jié)束,主線程就不會(huì)阻塞。

Invoke方法的主要功能就是幫助你在UI線程上調(diào)用委托所指定的方法。Invoke方法首先檢查發(fā)出調(diào)用的線程(即當(dāng)前線程)是不是UI線程,如果是,直接執(zhí)行委托指向的方法;如果不是,它將切換到UI線程,然后執(zhí)行委托指向的方法。不管當(dāng)前線程是不是UI線程,Invoke都阻塞直到委托指向的方法執(zhí)行完畢,然后切換回發(fā)出調(diào)用的線程(如果需要的話),返回。

BeginInvoke : 開(kāi)始一個(gè)異步的請(qǐng)求,調(diào)用線程池(ThreadPool )中一個(gè)線程來(lái)執(zhí)行,方法調(diào)后立即返回,不用等待委托方法的執(zhí)行結(jié)束,主線程就不會(huì)阻塞。Control.BeginInvoke方法并不會(huì)另開(kāi)線程。

返回 IAsyncResult 對(duì)象(異步的核心):IAsyncResult 簡(jiǎn)單的說(shuō),他是存儲(chǔ)異步操作狀態(tài)信息的一個(gè)接口,也可以用他來(lái)結(jié)束當(dāng)前異步。

注意: BeginInvoke和EndInvoke必須成對(duì)調(diào)用。即使不需要返回值,但EndInvoke還是必須調(diào)用,否則可能會(huì)造成內(nèi)存泄漏。

IAsyncResult.AsyncState 屬性:獲取用戶定義的對(duì)象,它限定或包含關(guān)于異步操作的信息。

首先,通過(guò)代碼定義一個(gè)委托和下面三個(gè)示例將要調(diào)用的方法:


? ? ? ? //委托

??????? public delegate int AddDel( int a, int b);

??????? //加法

??????? static int Add( int a, int b)

??????? {

??????????? Console .WriteLine("開(kāi)始計(jì)算:" + a + "+" + b);

??????????? Thread .Sleep(3000);//模擬該方法運(yùn)行三秒

??????????? Console .WriteLine("計(jì)算完成!" );

??????????? return a + b;

??????? }

同步調(diào)用

委托的Invoke方法用來(lái)進(jìn)行同步調(diào)用。同步調(diào)用也可以叫阻塞調(diào)用,它將阻塞當(dāng)前線程,然后執(zhí)行調(diào)用,調(diào)用完畢后再繼續(xù)向下進(jìn)行。

同步調(diào)用會(huì)阻塞線程,如果是要調(diào)用一項(xiàng)繁重的工作(如大量IO操作),可能會(huì)讓程序停頓很長(zhǎng)時(shí)間,造成糟糕的用戶體驗(yàn),這時(shí)候異步調(diào)用就很有必要了。

? ? ? ? static void Main(string [] args)

??????? {

??????????? Console .WriteLine("===== 同步調(diào)用 SyncInvokeTest =====" );

??????????? AddDel del = new AddDel(Add);

??????????? int result = del.Invoke(1, 2);

??????????? Console .WriteLine("繼續(xù)做別的事情。。。" );

??????????? Console .WriteLine(result);

??????????? Console .ReadKey();

? ? ? ? }

異步調(diào)用

異步調(diào)用不阻塞線程,而是把調(diào)用塞到線程池中,程序主線程或UI線程可以繼續(xù)執(zhí)行。委托的異步調(diào)用通過(guò)BeginInvoke和EndInvoke來(lái)實(shí)現(xiàn)。

可以看到,主線程并沒(méi)有等待,而是直接向下運(yùn)行了。但是問(wèn)題依然存在,當(dāng)主線程運(yùn)行到EndInvoke時(shí),如果這時(shí)調(diào)用沒(méi)有結(jié)束(這種情況很可能出現(xiàn)),這時(shí)為了等待調(diào)用結(jié)果,線程依舊會(huì)被阻塞。

? ? ? ? static void Main()

??????? {

??????????? Console .WriteLine("===== 異步調(diào)用 AsyncInvokeTest =====" );

??????????? AddDel del = new AddDel(Add);

??????????? //IAsyncResult: 異步操作接口(interface)

??????????? //BeginInvoke: 委托(delegate)的一個(gè)異步方法的開(kāi)始

??????????? IAsyncResult result = del.BeginInvoke(1, 2, null , null);

??????????? Console .WriteLine("繼續(xù)做別的事情......" );

??????????? //異步操作返回

??????????? Console .WriteLine(del.EndInvoke(result));

??????????? Console .ReadKey();

??????? }

異步回調(diào)

用回調(diào)函數(shù),當(dāng)調(diào)用結(jié)束時(shí)會(huì)自動(dòng)調(diào)用回調(diào)函數(shù),解決了為等待調(diào)用結(jié)果,而讓線程依舊被阻塞的局面。


? ? ? ? static void Main()

????????{

????????????Console .WriteLine("===== 異步回調(diào) AsyncInvokeTest =====" );

????????????AddDel del = new AddDel(Add);

????????????//異步操作接口(注意BeginInvoke方法的不同!)

????????????del.BeginInvoke(1, 2, new AsyncCallback (AddAsync), "AsyncState:OK");

????????????Console .WriteLine("繼續(xù)做別的事情。。。" );

????????????Console .ReadKey();

????????}

????????///

????????/// 回調(diào)函數(shù)

????????///

????????static void AddAsync(IAsyncResult ar)

????????{

????????????//AsyncResult 是IAsyncResult接口的一個(gè)實(shí)現(xiàn)類,空間:? ? ? ? ? ? ? ? ? ? ? ? System.Runtime.Remoting.Messaging

????????????//AsyncDelegate 屬性可以強(qiáng)制轉(zhuǎn)換為用戶定義的委托的實(shí)際類。

????????????AddDel del = ((AsyncResult )ar).AsyncDelegate as AddDel;

????????????Console .WriteLine(del.EndInvoke(ar));

????????????Console .WriteLine(ar.AsyncState);

????????}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實(shí)習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂(lè)視家的研發(fā)崗...
    時(shí)芥藍(lán)閱讀 42,757評(píng)論 11 349
  • 簡(jiǎn)易線程控制方法: 新建線程,無(wú)返回值,不帶參 Thread thread = new Thread(new Th...
    鑄劍悄悄對(duì)你說(shuō)閱讀 925評(píng)論 1 0
  • 命由己造,相由心生,世間萬(wàn)物皆是化相,心不動(dòng),萬(wàn)物皆不動(dòng),心不變,萬(wàn)物皆不變。由此可見(jiàn),有什么樣的心就有什么樣的相...
    艾米小宇宙閱讀 1,126評(píng)論 0 2
  • 認(rèn)識(shí)絢是在一次培訓(xùn)課上,她是我們的授課講師。 初見(jiàn)她,大家都驚得掉了下巴,誰(shuí)也想不到這個(gè)身材嬌小,一身休閑打扮的小...
    維多and利亞閱讀 284評(píng)論 5 2
  • 今天是我生日,彈指間三十幾年就這樣晃過(guò),細(xì)細(xì)想來(lái)依舊是一事無(wú)成。 我們上學(xué)時(shí),老師總喜歡要求我們寫(xiě)學(xué)習(xí)計(jì)劃,每次都...
    在水一方含閱讀 231評(píng)論 0 1

友情鏈接更多精彩內(nèi)容