背景:在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);
????????}