C 跨執行緒呼叫窗體控制項

2021-05-11 10:04:25 字數 4283 閱讀 6057

前段時間遇到跨執行緒呼叫窗體控制項的問題,其實一句話system.windows.forms.control.checkforillegalcrossthreadcalls = false;就可以解決,但感覺會有不穩定因素,因此在網上找了一些相應的文章感覺還不錯,第一種用的比較順手:

(注:在devexpress控制項中用devexpress.data.currencydatacontroller.disablethreadingproblemsdetection = true;)

不過windows 窗體體系結構對執行緒使用制定了嚴格的規則。如果只是編寫單執行緒應用程式,則沒必要知道這些規則,這是因為單執行緒的**不可能違反這些規則。然而,一旦採用多執行緒,就需要理解 windows 窗體中最重要的一條執行緒規則:除了極少數的例外情況,否則都不要在它的建立執行緒以外的執行緒中使用控制項的任何成員。本規則的例外情況有文件說明,但這樣的情況非常少。這適用於其類派生自 system.windows.forms.control 的任何物件,其中幾乎包括 ui 中的所有元素。所有的 ui 元素(包括表單本身)都是從 control 類派生的物件。此外,這條規則的結果是乙個被包含的控制項(如,包含在乙個表單中的按鈕)必須與包含它控制項位處於同乙個執行緒中。也就是說,乙個視窗中的所有控制項屬於同乙個 ui 執行緒。實際中,大部分 windows 窗體應用程式最終都只有乙個執行緒,所有 ui 活動都發生在這個執行緒上。這個執行緒通常稱為 ui 執行緒。這意味著您不能呼叫使用者介面中任意控制項上的任何方法,除非在該方法的文件說明中指出可以呼叫。該規則的例外情況(總有文件記錄)非常少而且它們之間關係也不大。請注意,以下**是非法的:

private thread mythread;

private void form1_load(object sender, eventargs e)

mythread = new thread(new threadstart(runsonworkerthread));

mythread.start();

private void runsonworkerthread()

label1.text = "mythread執行緒呼叫ui控制項";

如果您在 .net framework 1.0 版本中嘗試執行這段**,也許會僥倖執行成功,或者初看起來是如此。這就是多執行緒錯誤中的主要問題,即它們並不會立即顯現出來。甚至當出現了一些錯誤時,在第一次演示程式之前一切看起來也都很正常。但不要搞錯 — 我剛才顯示的這段**明顯違反了規則,並且可以預見,任何抱希望於「試執行時良好,應該就沒有問題」的人在即將到來的除錯期是會付出沉重代價的。

下面我們來看看有哪些方法可以解決這一問題。

一、system.windows.forms.methodinvoker 型別是乙個系統定義的委託,用於呼叫不帶引數的方法。

private thread mythread;

private void form1_load(object sender, eventargs e)

mythread = new thread(new threadstart(runsonworkerthread));

mythread.start();

private void runsonworkerthread()

methodinvoker mi = new methodinvoker(setcontrolsprop);

begininvoke(mi);

private void setcontrolsprop()

label1.text = "mythread執行緒呼叫ui控制項";

二、直接用system.eventhandle(可帶引數)

private thread mythread;

private void form1_load(object sender, eventargs e)

mythread = new thread(new threadstart(runsonworkerthread));

mythread.start();

private void runsonworkerthread()

//dosomethingslow();

string plist = "mythread執行緒呼叫ui控制項";

label1.begininvoke(new system.eventhandler(updateui), plist);

//直接用system.eventhandler,沒有必要自定義委託

private void updateui(object o, system.eventargs e)

//ui執行緒設定label1屬性

label1.text = o.tostring() + "成功!";

三、包裝 control.invoke

雖然第二個方法中的**解決了這個問題,但它相當繁瑣。如果輔助線程希望在結束時提供更多的反饋資訊,而不是簡單地給出「finished!」訊息,則 begininvoke 過於複雜的使用方法會令人生畏。為了傳達其他訊息,例如「正在處理」、「一切順利」等等,需要設法向 updateui 函式傳遞乙個引數。可能還需要新增乙個進度欄以提高反饋能力。這麼多次呼叫 begininvoke 可能導致輔助線程受該**支配。這樣不僅會造成不便,而且考慮到輔助線程與 ui 的協調性,這樣設計也不好。對這些進行分析之後,我們認為包裝函式可以解決這兩個問題。

private thread mythread;

private void form1_load(object sender, eventargs e)

mythread = new thread(new threadstart(runsonworkerthread));

mythread.start();

private void runsonworkerthread()

dosomethingslow();

for (int i = 0; i < 100; i++)

showprogress( convert.tostring(i)+"%", i);

thread.sleep(100);

public void showprogress(string msg, int percentdone)

// wrap the parameters in some eventargs-derived custom class:

system.eventargs e = new myprogressevents(msg, percentdone);

object plist = ;

begininvoke(new myprogresseventshandler(updateui), plist);

private delegate void myprogresseventshandler(object sender, myprogressevents e);

private void updateui(object sender, myprogressevents e)

lblstatus.text = e.msg;

myprogresscontrol.value = e.percentdone;

public class myprogressevents : eventargs

public string msg;

public int percentdone;

public myprogressevents(string msg, int per)

msg = msg;

percentdone = per;

showprogress 方法對將呼叫引向正確執行緒的工作進行封裝。這意味著輔助線程**不再擔心需要過多關注 ui 細節,而只要定期呼叫 showprogress 即可。

如果我提供乙個設計為可從任何執行緒呼叫的公共方法,則完全有可能某人會從 ui 執行緒呼叫這個方法。在這種情況下,沒必要呼叫 begininvoke,因為我已經處於正確的執行緒中。呼叫 invoke 完全是浪費時間和資源,不如直接呼叫適當的方法。為了避免這種情況,control 類將公開乙個稱為 invokerequired 的屬性。這是「只限 ui 執行緒」規則的另乙個例外。它可從任何執行緒讀取,如果呼叫執行緒是 ui 執行緒,則返回假,其他執行緒則返回真。這意味著我可以按以下方式修改包裝:

public void showprogress(string msg, int percentdone)

if (invokerequired)

// as before

else

// we're already on the ui thread just

// call straight through.

updateui(this, new myprogressevents(msg,percentdone));

C 跨執行緒呼叫窗體控制項的問題

前段時間遇到跨執行緒呼叫窗體控制項的問題,其實一句話system.windows.forms.control.checkforillegalcrossthreadcalls false 就可以解決,但感覺會有不穩定因素,因此在網上找了一些相應的文章感覺還不錯,第一種用的比較順手 注 在devexpr...

如何跨執行緒呼叫winform窗體控制項

方法一 直接關閉異常,不檢查跨執行緒呼叫private void form1 load object sender,eventargs e 方法二 利用委託 舉例1 delegate void tasktype string sztext 這裡是因為執行緒的方法只能是object型別 void th...

C 跨執行緒呼叫控制項

在c 應用程式開發中,我們經常需要把ui執行緒和工作執行緒分開程式設計,為了防止介面停止響應。同時,我們也需要在工作執行緒中去更新ui介面的控制項,在clr的執行緒安全中並不允許我們直接在工作執行緒操作ui介面。因此,介紹以下三種方式進行跨執行緒操作ui。private void button2 c...