如何跨越執行緒呼叫窗體上的控制項

2021-04-13 02:31:26 字數 3380 閱讀 2486

ui

的響應速度

,使得一切執行顯得更為快速。在

windows

中進行多執行緒程式設計曾經是

c++

開發人員的專屬特權,但是現在,可以使用所有相容

microsoft .net

的語言來編寫。 不過

windows

窗體體系結構對執行緒使用制定了嚴格的規則。如果只是編寫單執行緒應用程式,則沒必要知道這些規則,這是因為單執行緒的**不可能違反這些規則。然而,一旦採用多執行緒,就需要理解

windows

窗體中最重要的一條執行緒規則:除了極少數的例外情況,否則都不要在它的建立執行緒以外的執行緒中使用控制項的任何成員。本規則的例外情況有文件說明,但這樣的情況非常少。這適用於其類派生自

system.windows.forms.control

的任何物件,其中幾乎包括

ui 中的所有元素。所有的

ui 元素(包括表單本身)都是從

control

類派生的物件。此外,這條規則的結果是乙個被包含的控制項(如,包含在乙個表單中的按鈕)必須與包含它控制項位處於同乙個執行緒中。也就是說,乙個視窗中的所有控制項屬於同乙個

ui 執行緒。實際中,大部分

windows

窗體應用程式最終都只有乙個執行緒,所有

ui 活動都發生在這個執行緒上。這個執行緒通常稱為

ui 執行緒。這意味著您不能呼叫使用者介面中任意控制項上的任何方法,除非在該方法的文件說明中指出可以呼叫。該規則的例外情況(總有文件記錄)非常少而且它們之間關係也不大。請注意,以下**是非法的:

private

thread mythread;

private

void form1_load(object sender, eventargs e)

private

void runsonworkerthread()

如果您在

.net framework 1.0

版本中嘗試執行這段**,也許會僥倖執行成功,或者初看起來是如此。這就是多執行緒錯誤中的主要問題,即它們並不會立即顯現出來。甚至當出現了一些錯誤時,在第一次演示程式之前一切看起來也都很正常。但不要搞錯

—我剛才顯示的這段**明顯違反了規則,並且可以預見,任何抱希望於「試執行時良好,應該就沒有問題」的人在即將到來的除錯期是會付出沉重代價的。

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

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

private

thread mythread;

private

void form1_load(object sender, eventargs e)

private

void runsonworkerthread()

private

void setcontrolsprop()

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

private

thread mythread;

private

void form1_load(object sender, eventargs e)

private

void runsonworkerthread()

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

private

void updateui(object o, system.eventargs e)

三、包裝 control.invoke

雖然第二個方法中的**解決了這個問題,但它相當繁瑣。如果輔助線程希望在結束時提供更多的反饋資訊,而不是簡單地給出「

finished!

」訊息,則

begininvoke

過於複雜的使用方法會令人生畏。為了傳達其他訊息,例如「正在處理」、「一切順利」等等,需要設法向

updateui

函式傳遞乙個引數。可能還需要新增乙個進度欄以提高反饋能力。這麼多次呼叫

begininvoke

可能導致輔助線程受該**支配。這樣不僅會造成不便,而且考慮到輔助線程與

ui 的協調性,這樣設計也不好。對這些進行分析之後,我們認為包裝函式可以解決這兩個問題。

private

thread mythread;

private

void form1_load(object sender, eventargs e)

private

void runsonworkerthread()

} public

void showprogress(string msg, int percentdone) ;

begininvoke(new

myprogresseventshandler(updateui), plist); }

private

delegate

void

myprogresseventshandler(object sender, myprogressevents e);

private

void updateui(object sender, myprogressevents e)

public

class

myprogressevents : eventargs }

showprogress

方法對將呼叫引向正確執行緒的工作進行封裝。這意味著輔助線程**不再擔心需要過多關注

ui 細節,而只要定期呼叫

showprogress

即可。

如果我提供乙個設計為可從任何執行緒呼叫的公共方法,則完全有可能某人會從

ui 執行緒呼叫這個方法。在這種情況下,沒必要呼叫

begininvoke

,因為我已經處於正確的執行緒中。呼叫

invoke

完全是浪費時間和資源,不如直接呼叫適當的方法。為了避免這種情況,

control

類將公開乙個稱為

invokerequired

的屬性。這是「只限

ui 執行緒」規則的另乙個例外。它可從任何執行緒讀取,如果呼叫執行緒是

ui 執行緒,則返回假,其他執行緒則返回真。這意味著我可以按以下方式修改包裝:

public

void showprogress(string msg, int percentdone)

else }

如何跨越執行緒呼叫窗體上的控制項

ui 的響應速度 使得一切執行顯得更為快速。在 windows 中進行多執行緒程式設計曾經是 c 開發人員的專屬特權,但是現在,可以使用所有相容 microsoft net 的語言來編寫。不過 windows 窗體體系結構對執行緒使用制定了嚴格的規則。如果只是編寫單執行緒應用程式,則沒必要知道這些規...

如何跨越執行緒呼叫窗體控制項?(1)

一 前言 二 本節介紹最簡單的方法。在主線程中,定義乙個子執行緒處理的物件,通過主線程啟動子執行緒。然後通過主線程,不斷的獲取最新的處理狀態。當然,也可以定義乙個全域性變數,子執行緒不斷更新該全域性變數,而主線程定時去獲取狀態資訊。主線程不斷獲取子執行緒的處理狀態,可以通過定時器來完成。注意,子執行...

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

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