现在的位置: 首页 > 综合 > 正文

C#中Invoke的用法(转)

2012年02月15日 ⁄ 综合 ⁄ 共 1669字 ⁄ 字号 评论关闭
 

在多线程编程中,我们经常要在工作线程中去更新界面显示,而在多线程中直接调用界面控件的方法是错误的做法,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();

}

}

}

注意代理的使用!

抱歉!评论已关闭.