C#中Invoke的用法(轉)


 

在多線程編程中,我們經常要在工作線程中去更新界面顯示,而在多線程中直接調用界面控件的方法是錯誤的做法,Invoke 和 BeginInvoke 就是為了解決這個問題而出現的,使你在多線程中安全的更新界面顯示。

正確的做法是將工作線程中涉及更新界面的代碼封裝為一個方法,通過 Invoke 或者 BeginInvoke 去調用,兩者的區別就是一個導致工作線程等待,而另外一個則不會。

 

Framework框架的WinForm構建GUI程序界面時,如果要在控件的事件響應函數中改變控件的狀態

,例如:某個按鈕上的文本原先叫“打開”,單擊之后按鈕上的文本顯示“關閉”,初學者往往會想當然地這么寫:

void ButtonOnClick(object sender,EventArgs e)

{

button.Text="關閉";

}

這樣的寫法運行程序之后,可能會觸發異常,異常信息大致是“不能從不是創建該控件的線程調用它”。注意這里是“可能”,並不一定會觸發該種異常。造 成這種異常的原因在於,控件是在主線程中創建的(比如this.Controls.Add(...);),進入控件的事件響應函數時,是在控件所在的線 程,並不是主線程。在控件的事件響應函數中改變控件的狀態,可能與主線程發生線程沖突。如果主線程正在重繪控件外觀,此時在別的線程改變控件外觀,就會造 成畫面混亂。不過這樣的情況並不總會發生,如果主線程此時在重繪別的控件,就可能逃過一劫,這樣的寫法可以正常通過,沒有觸發異常。

正確的寫法是在控件響應函數中調用控件的Invoke方法(其實如果大家以前用過C++

Builder的話,也會找到類似Invoke那樣的激活到主線程的函數)。Invoke方法會順着控件樹向上搜索,直到找到創建控件的那個線程(通常是主線程),然后進入那個線程改變控件的外觀,確保不發生線程沖突。

 


而所謂的“一面響應操作,一面添加節點”永遠只能是相對的,使 UI 線程的負擔不至於太大而已,因為界面的正確更新始終要通過 UI 線程去做,我們要做的事情是在工作線程中包攬大部分的運算,而將對純粹的界面更新放到 UI 線程中去做,這樣也就達到了減輕 UI 線程負擔的目的了。

舉個簡單例子說明下使用方法,比如你在啟動一個線程,在線程的方法中想更新窗體中的一個TextBox.. 



using System.Threading; 

//啟動一個線程 
Thread thread=new Thread(new ThreadStart(DoWork)); 
thread.Start(); 

//線程方法 
private void DoWork() 

this.TextBox1.Text="我是一個文本框"; 


如果你像上面操作,在VS2005或2008里是會有異常的... 

正確的做法是用Invoke\BeginInvoke

using System.Threading;
namespace test
{
public partial class Form1 : Form
{
public delegate void MyInvoke(string str1,string str2);
public Form1()
{
InitializeComponent();


}
public void DoWork()
{
MyInvoke mi = new MyInvoke(UpdateForm);
this.BeginInvoke(mi, new Object[] {"我是文本框","haha"});
}
public void UpdateForm(string param1,string parm2)
{
this.textBox1.Text = param1+parm2;
}
private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(new ThreadStart(DoWork));
thread.Start();
}
}
}
注意代理的使用!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM