在UI線程執(zhí)行代碼(.Net)
好久沒(méi)寫(xiě)博客了。最近在重構(gòu)代碼,碰到了很多了有意義的問(wèn)題,在此和大家分享。
大家知道,在使用異步IO或者大量多線程代碼時(shí),總會(huì)碰到同步問(wèn)題,例如在其他線程調(diào)用Winform的Control相關(guān)代碼,就會(huì)導(dǎo)致異常。最近發(fā)現(xiàn)NAudio的WaveIn和WaveOut居然也需要使用UI線程的消息結(jié)構(gòu)才能正常工作,他們會(huì)在后臺(tái)創(chuàng)建不可見(jiàn)窗口。如果你在Socket的異步IO回調(diào)中直接調(diào)用Wave系列的方法,則會(huì)異常或者沒(méi)有任何反應(yīng)。(BeginXXX系列等異步方法的回調(diào)是在線程池中某個(gè)線程中調(diào)用的)
一般Windows程序的UI線程只有一個(gè),UI相關(guān)的更新都應(yīng)該發(fā)生在UI線程上,才能保證安全。因此實(shí)際工作中,我們要學(xué)會(huì)怎么在一個(gè)非UI線程中轉(zhuǎn)向UI線程中執(zhí)行代碼(或者通知UI線程執(zhí)行某段代碼),下面我就說(shuō)兩種基本的方法:
1. 使用WinForm的Control.Invoke
WinForm的每個(gè)Control都有一個(gè)屬性(InvokeRequired)和一個(gè)方法(Invoke)用來(lái)在UI線程執(zhí)行代碼。Control.InvokeRequired屬性指示當(dāng)前線程是不是創(chuàng)建Control的線程。所以這種方法一般有以下的結(jié)構(gòu)
1 ... 2 3 if (anyControl.InvokeRequired) 4 { 5 anyControl.Invoke(new Action(delegate{ myControl.DoSomething(); })); 6 }
注意Invoke可以接受任何Delegate,但是那個(gè)Delegate必須是某一個(gè)Delegate:如果上面的代碼沒(méi)有new Action或者new MethodInvoker之類(lèi)的具體轉(zhuǎn)化,C#會(huì)因?yàn)椴恢烙媚膫€(gè)Delegate而報(bào)錯(cuò),因?yàn)楹芏郉elegate都符合這個(gè)沒(méi)參數(shù)沒(méi)返回值的signature。如果delegate有返回值,那么Invoke方法會(huì)返回那個(gè)值。Control還實(shí)現(xiàn)了BeginInvoke和EndInvoke執(zhí)行異步調(diào)用,但一般不會(huì)用到。
這個(gè)方法有一個(gè)限制,就是你的上下文中有對(duì)UI控件的引用。很多情況下后臺(tái)代碼并沒(méi)有對(duì)UI控件的引用,就很不方便。
2. 使用SynchronizationContext
System.Threading.SynchronizationContext是.Net平臺(tái)下終極的線程間互相執(zhí)行代碼的機(jī)制。我在這里只是講講一下它在UI線程中的用法,其他的大家就看MSDN吧:)
使用SynchronizationContext.Current可以獲得當(dāng)前線程的SynchronizationContext。并不是所有的線程都有SynchronizationContext,但是WinForm的UI線程肯定有一個(gè)(微軟保證的),所以在程序開(kāi)始的時(shí)候我們可以在Form的初始化代碼中獲得UI線程的Context,存在變量中,也可以傳給后臺(tái)代碼。之后我們想在UI線程執(zhí)行代碼(例如更新界面顯示),就可以調(diào)用下面兩個(gè)方法:
1 // 同步執(zhí)行代碼,等待返回,第二個(gè)參數(shù)是方法參數(shù),沒(méi)有的話用null 2 m_uiContext.Send(delegate {DoSomething();}, null); 3 // 異步執(zhí)行代碼,立刻返回。第二個(gè)參數(shù)同上 4 m_uiContext.Post(delegate {DoSomething();}, null);
兩種方式一般沒(méi)有什么差別,因?yàn)槲覀儽M量保持UI線程的操作盡量很少,保證UI不會(huì)Freeze,因此都會(huì)很快執(zhí)行完畢。
以上簡(jiǎn)單的介紹希望有用。如果誰(shuí)還知道其他的方法,歡迎提出,嘿嘿;)

浙公網(wǎng)安備 33010602011771號(hào)