在用.net framework框架的winform構建gui程式介面時,如果要在控制項的事件響應函式中改變控制項的狀態,例如:某個按鈕上的文字原先叫「開啟」,單擊之後按鈕上的文字顯示「關閉」,初學者往往會想當然地這麼寫:
void buttononclick(object sender,eventargs e)
button.text="關閉";
這樣的寫法執行程式之後,可能會觸發異常,異常資訊大致是「不能從不是建立該控制項的執行緒呼叫它」。注意這裡是「可能」,並不一定會觸發該種異常。造成這種異常的原因在於,控制項是在主線程中建立的(比如this.controls.add(...);),進入控制項的事件響應函式時,是在控制項所在的執行緒,並不是主線程。在控制項的事件響應函式中改變控制項的狀態,可能與主線程發生執行緒衝突。如果主線程正在重繪控制項外觀,此時在別的執行緒改變控制項外觀,就會造成畫面混亂。不過這樣的情況並不總會發生,如果主線程此時在重繪別的控制項,就可能逃過一劫,這樣的寫法可以正常通過,沒有觸發異常。
正確的寫法是在控制項響應函式中呼叫控制項的invoke方法(其實如果大家以前用過c++ builder的話,也會找到類似invoke那樣的啟用到主線程的函式)。invoke方法會順著控制項樹向上搜尋,直到找到建立控制項的那個執行緒(通常是主線程),然後進入那個執行緒改變控制項的外觀,確保不發生執行緒衝突。正確寫法的示例如下:
void buttononclick(object sender,eventargs e)
button.invoke(new eventhandler(delegate
button.text="關閉";
invoke方法需要建立乙個委託。你可以事先寫好函式和與之對應的委託。不過,若想直觀地在invoke方法呼叫的時候就看到具體的函式,而不是到別處搜尋的話,上面的示例**是不錯的選擇。
這樣的寫法有乙個煩人的地方:對不同的控制項寫法不同。對於textbox,要textboxobject.invoke,對於label,又要labelobject.invoke。有沒有統一一點的寫法呢?
主視窗類本身也有invoke方法。如果你不想對不同的控制項寫法不一樣,可以全部用this.invoke:
void buttononclick(object sender,eventargs e)
this.invoke(new eventhandler(delegate
button.text="關閉";
在c# 3.0及以後的版本中有了lamda表示式,像上面這種匿名委託有了更簡潔的寫法。.net framework 3.5及以後版本更能用action封裝方法。例如以下寫法可以看上去非常簡潔:
void buttononclick(object sender,eventargs e)
this.invoke(new action(()=>
button.text="關閉";
以上寫法往往充斥著winform構建的程式。
在微軟新一代的介面開發技術wpf中,由於介面呈現和業務邏輯原生態地分開在兩個執行緒中,所以控制項的事件響應函式就不必invoke了。但是,如果手動開闢乙個新執行緒,那麼在這個新執行緒中改變控制項的外觀,則還是要invoke的。
當乙個控制項的invokerequired屬性值為真時,說明有乙個建立它以外的執行緒想訪問它,此時它將會在內部呼叫new methodinvoker(loadglobalimage)來完成下面的步驟,這個做法保證了控制項的安全,你可以這樣理解,有人想找你借錢,他可以直接在你的錢包
中拿,這樣太不安全,因此必須讓別人先要告訴你,你再從自己的錢包把錢拿出來借給別人,這樣就安全了
another:
在設計中為了讓介面與邏輯分離,我的做法是使用事件,介面只要響應事件來處理介面的顯示就行了。而事件在邏輯處理中可能由不同的執行緒引發,這些事件的響應方法在修改介面中的控制項內容時便會引發乙個異常。
這時就用到了control.invokerequired 屬性 與invoke方法。
msdn中說:
獲取乙個值,該值指示呼叫方在對控制項進行方法呼叫時是否必須呼叫 invoke 方法,因為呼叫方位於建立控制項所在的執行緒以外的執行緒中。
如果控制項的 handle 是在與呼叫執行緒不同的執行緒上建立的(說明您必須通過 invoke 方法對控制項進行呼叫),則為 true;否則為 false。
windows 窗體中的控制項被繫結到特定的執行緒,不具備執行緒安全性 。因此,如果從另乙個執行緒呼叫控制項的方法,那麼必須使用控制項的乙個 invoke 方法來將呼叫封送到適當的執行緒。該屬性可用於確定是否必須呼叫 invoke 方法,當不知道什麼執行緒擁有控制項時這很有用。
下面來說下這個的用法(我的一般做法):
首先定義乙個委託,與這個事件處理函式的簽名一樣委託,當然直接使用該事件的委託也是可以的,如:
然後就是判斷這個屬性的值來決定是否要呼叫invoke函式:
說明:這個函式就是事件處理函式,txtmessage是乙個文字框。
這樣就做到了窗體中控制項的執行緒安全性。
我理解:如果invokerequired==true表示其它執行緒需要訪問控制項,那麼呼叫invoke來轉給控制項owner處理。
C 中Invoke的用法
在多執行緒程式設計中,我們經常要在工作執行緒中去更新介面顯示,而在多執行緒中直接呼叫介面控制項的方法是錯誤的做法,invoke 和 begininvoke 就是為了解決這個問題而出現的,使你在多執行緒中安全的更新介面顯示。正確的做法是將工作執行緒中涉及更新介面的 封裝為乙個方法,通過 invoke ...
C 中Invoke的用法
在用.net framework框架的winform構建gui程式介面時,如果要在控制項的事件響應函式中改變控制項的狀態,例如 某個按鈕上的文字原先叫 開啟 單擊之後按鈕上的文字顯示 關閉 初學者往往會想當然地這麼寫 void buttononclick object sender,eventarg...
C 中Invoke的用法()
一直對invoke和begininvoke的使用和概念比較混亂,這兩天看了些資料,對這兩個的用法和原理有了些新的認識和理解。首先說下,invoke和begininvoke的使用有兩種情況 1.control中的invoke begininvoke。2.delegrate中的invoke begini...