C#中Invoke的用法---------多线程操作:线程间操作无效,从不是创建控件的线程访问它
设计一个界面,点击button之后,改变label的值
实现:点击button执行button的click事件,在函数下创建一个线程change,然后在这个线程中执行改变label的方法ChangeLabel();
说明:控件是在UI主线程中创建的,进入控件的事件响应函数时,是在控件所在的线程,并不是主线程。在控件的事件响应函数中改变控件的状态,可能与主线程发生线程冲突。
若直接用以下的写法,必然会出错:
private void btnChange_Click(object sender, EventArgs e)
{
Thread Change = new Thread(new ThreadStart(ChangeLabel));
Change.Name = "zyh";
Change.Start();
}
private void ChangeLabel()
{
try
{
label.Text = "改变值";
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,"错误提示",MessageBoxButtons.YesNo,MessageBoxIcon.Error);
}
}
解决方法:
正确的写法是在控件响应函数中调用控件的Invoke方法。Invoke方法会顺着控件向上搜索,直到找到创建控件的那个线程(通常是主线程),然后进入那个线程改变控件的外观,确保不发生线程冲突。正确写法的示例有以下三种:
第一种:
this.Invoke(new Action(() =>
{label.Text = “关闭”; }
));
第二种:
this.Invoke(new Action(
delegate { label.Text = “关闭”; }
));
第三种:
当一个控件的InvokeRequired属性值为真时,说明有一个创建它以外的线程想访问它
MSDN中说:
获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。 如果控件的 Handle 是在与调用线程不同的线程上创建的(说明您必须通过 Invoke 方法对控件进行调用),则为 true;否则为 false。Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性 。因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
private void ChangeLabel()
{
try
{
if (label.InvokeRequired)
{
label.Invoke(new MethodInvoker(ChangeLabel));
}
else
{
label.Text = "改变值";
}
}
catch (Exception ex)
{
MessageBox.Show(ex.Message,"错误提示",MessageBoxButtons.YesNo,MessageBoxIcon.Error);
}
}
程序执行:
点击按钮,系统判断,有一个创建label线程以外的线程想访问它,此时InvokeRequired属性为真,调用Invoke方法,在执行一遍ChangeLabel()方法,跳入else中,执行改变label的值,这时候便成功了
简单的说,如果有两个线程,Thread A和Thread B,并且有一个Control c,是在Thread A里面new的。那么在Thread A里面运行的任何方法调用c.InvokeRequired都会返回false。
相反,如果在Thread B里面运行的任何方法调用c.InvokeRequired都会返回true。
是否是UI线程与结果无关。(通常Control所在的线程是UI线程,但是可以有例外)
参考博文:
https://www.cnblogs.com/vaevvaev/p/6909042.html
上一篇: Facebook图片管理架构
下一篇: 大数据应用对教育领域正在发生革命性影响