【上位機(jī)入門(mén)常見(jiàn)問(wèn)題】跨線程訪問(wèn)與Action

一、背景
很多C#初學(xué)者,都遇到過(guò)這樣的問(wèn)題,今天就這個(gè)問(wèn)題,進(jìn)行分析。

二、根源
先說(shuō)下這個(gè)問(wèn)題產(chǎn)生的根源,大家都知道,程序運(yùn)行起來(lái)之后,首先會(huì)有一個(gè)主線程,主線程用于處理控件生成、界面渲染、事件響應(yīng)等操作,因此我們可以理解為窗體里的控件是屬于主線程的。
如果我們想實(shí)現(xiàn)與主線程同時(shí)執(zhí)行另一件事,一般會(huì)去使用多線程,因此多線程,從某種意義上來(lái)說(shuō),它和主線程的身份是平等的,就像你和你的同事的關(guān)系一樣,你們是平級(jí)的關(guān)系。如果你同事有一天想從你手上把你的項(xiàng)目程序拿過(guò)去,你愿不愿意?
所以,如果在多線程里操作主線程的控件,你覺(jué)得主線程會(huì)不會(huì)答應(yīng),當(dāng)然不會(huì),所以,它就會(huì)給你報(bào)個(gè)錯(cuò),給你一個(gè)警告!

三、分析
那么如何解決呢?
所以你的同事就會(huì)找到你們領(lǐng)導(dǎo),跟你們領(lǐng)導(dǎo)這樣說(shuō):我手頭上的這個(gè)100萬(wàn)的項(xiàng)目,能給公司帶來(lái)50%的利潤(rùn),現(xiàn)在需要用到他之前那個(gè)項(xiàng)目里的一個(gè)小知識(shí),需要他把程序給我參考一下。
于是,領(lǐng)導(dǎo)和你“商量”了一下,你便妥協(xié)了。
你那個(gè)同事利用的招數(shù)叫做—————委托。
現(xiàn)在多線程要干這個(gè)事,也要靠委托來(lái)做。
四、委托
委托定義:委托(Delegate) 是對(duì)某個(gè)方法的引用的一種引用類(lèi)型變量。
1、聲明委托
委托聲明需要根據(jù)執(zhí)行的方法來(lái)定,嚴(yán)格來(lái)說(shuō),就是根據(jù)執(zhí)行方法的返回值和參數(shù),我們只是給窗體的Text設(shè)置一個(gè)固定值而已,因此我們的參數(shù)是空,返回值也為空。
聲明委托如下:
????//聲明委托
????public?delegate?void?SetFormTextDelegate();
2、創(chuàng)建委托對(duì)象
委托嚴(yán)格來(lái)說(shuō)是一種類(lèi)型,就像類(lèi)一樣,如果想要調(diào)用某個(gè)類(lèi),必須要?jiǎng)?chuàng)建一個(gè)該類(lèi)的對(duì)象,所以我們要?jiǎng)?chuàng)建一個(gè)委托對(duì)象:
? //創(chuàng)建委托對(duì)象
? private SetFormTextDelegate SetFormText;
3、創(chuàng)建委托方法
委托對(duì)象也只是一個(gè)對(duì)象而已,就像領(lǐng)導(dǎo)一樣,領(lǐng)導(dǎo)是不可能干活的,最終干活還得靠底下的兵來(lái)干,所以我們還得招人去干活。
招人干活就是委托方法,我們現(xiàn)在這個(gè)活很簡(jiǎn)單,所以我們的方法也很簡(jiǎn)單。
? //執(zhí)行方法
? private void ExcuteMethod()
? {
? ? ? ?this.Text = "多線程測(cè)試";
? }
4、委托綁定
我們招到了一個(gè)“兵”,現(xiàn)在也有一個(gè)部門(mén)領(lǐng)導(dǎo),怎么把他們聯(lián)系起來(lái)呢?
很簡(jiǎn)單,讓人事把這個(gè)兵分到這個(gè)部門(mén)就行了,這個(gè)分配的過(guò)程就是委托綁定,代碼如下:
? //委托綁定
? this.SetFormText = ExcuteMethod;
5、委托調(diào)用
萬(wàn)事俱備,只欠東風(fēng),終于干活了。
作為公司的老板,一般是不可能跟員工打交道的,他會(huì)把任務(wù)分配給部門(mén)領(lǐng)導(dǎo),部門(mén)領(lǐng)導(dǎo)會(huì)把活再分配下去,所以我們委托調(diào)用,也是調(diào)用委托對(duì)象。
? ?/// <summary>
? ?/// 多線程方法
? ?/// </summary>
? ?private void ThreadMethod()
? ?{
? ? ? ?//調(diào)用委托
? ? ? ?SetFormText();
? ?}
以上五步,就是委托的實(shí)現(xiàn)過(guò)程。
然而,我們運(yùn)行之后,還是會(huì)報(bào)錯(cuò)。

沒(méi)有那么簡(jiǎn)單的事!
因?yàn)橄胍诙嗑€程里操作主線程的控件,你還得經(jīng)過(guò)控件的同意,怎么經(jīng)過(guò)控件同意呢?
控件的父類(lèi)Control提供了一個(gè)這樣的方法:

意思就是說(shuō),想要操作控件,必須要通過(guò)Invoke方法來(lái)實(shí)現(xiàn),Invoke方法里參數(shù)是一個(gè)委托,于是,我們只能灰溜溜地,這樣寫(xiě):

果然,按照規(guī)矩來(lái),就能達(dá)到效果:

五、簡(jiǎn)化
微軟從某個(gè)版本開(kāi)始,出來(lái)了Action和Lamda表達(dá)式,Action是系統(tǒng)委托,也就是說(shuō),不需要我們手動(dòng)創(chuàng)建委托了,它有個(gè)兄弟叫Func,Action沒(méi)有返回值,最多可以有16個(gè)參數(shù),F(xiàn)unc必須要有返回值,最多可以有16個(gè)參數(shù),最后一個(gè)參數(shù)表示返回值。
于是我們開(kāi)始簡(jiǎn)化:
第一步簡(jiǎn)化:用Action作為委托來(lái)創(chuàng)建
? ? ? ?/// <summary>
? ? ? ?/// 多線程方法
? ? ? ?/// </summary>
? ? ? ?private void ThreadMethod()
? ? ? ?{
? ? ? ? ? ?//創(chuàng)建委托、綁定委托
? ? ? ? ? ?Action action = new Action(ExcuteMethod);
? ? ? ? ? ?//調(diào)用委托
? ? ? ? ? ?this.Invoke(action);
? ? ? ?}
? ? ? ?
? ? ? ?//執(zhí)行方法
? ? ? ?private void ExcuteMethod()
? ? ? ?{
? ? ? ? ? ?this.Text = "多線程測(cè)試";
? ? ? ?}
第二步簡(jiǎn)化:委托對(duì)象只用一次,所以可以直接放到參數(shù)里
??? ? ?/// <summary>
? ? ? /// 多線程方法
? ? ? /// </summary>
? ? ? private void ThreadMethod()
? ? ? {
? ? ? ? ? //創(chuàng)建委托、綁定委托、調(diào)用委托
? ? ? ? ? this.Invoke(new Action(ExcuteMethod));
? ? ? }
? ? ? //執(zhí)行方法
? ? ? private void ExcuteMethod()
? ? ? {
? ? ? ? ? this.Text = "多線程測(cè)試";
? ? ? }
第三步簡(jiǎn)化:用Lamda表達(dá)式代替方法
? ? ? /// <summary>
? ? ? /// 多線程方法
? ? ? /// </summary>
? ? ? private void ThreadMethod()
? ? ? {
? ? ? ? ? //創(chuàng)建委托、綁定委托、調(diào)用委托
? ? ? ? ? this.Invoke(new Action(()=>
? ? ? ? ? {
? ? ? ? ? ? ? this.Text = "多線程測(cè)試";
? ? ? ? ? }));
? ? ? }
六、總結(jié)
我們所以常寫(xiě)的那行代碼,其實(shí)只是一種簡(jiǎn)寫(xiě)方式而已,委托的五步法,不管怎么簡(jiǎn)化,怎么優(yōu)化,其實(shí)本質(zhì)還是一樣,都離開(kāi)不了這五個(gè)步驟。
這就是經(jīng)典。
都看到這里了,是不是要點(diǎn)個(gè)贊呢?