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

发布了18 篇原创文章 · 获赞 0 · 访问量 233

猜你喜欢

转载自blog.csdn.net/qq_39217004/article/details/105496169