winform 跨线程访问问题

时间:2022-02-21 19:14:27

一、问题描述

进行winform 开发我们在进行数据交换时避免不了使用多线程或异步方法,这样操作也将避免不了跨线程对控件进行操作(赋值、修改属性)。

下面通过一个测试说明一下问题

winform  跨线程访问问题

点击一个按钮异步对textbox进行赋值

运行测试结果

winform  跨线程访问问题

 private void button1_Click(object sender, EventArgs e)
{
Action<string> action = new Action<string>((str) =>
{
// 解决跨线程赋值
this.textBox1.Text = str;
});
action.BeginInvoke("Test", null, null);
}

产生错误的原因:textBox1是由主线程创建的,异步方法(或子线程)是另外创建的一个线程,在.NET上执行的是托管代码,C#强制要求这些代码必须是线程安全的,即不允许跨线程访问Windows窗体的控件。

二、解决方法

1.在窗体的加载事件中,将C#内置控件(Control)类的CheckForIllegalCrossThreadCalls属性设置为false,屏蔽掉C#编译器对跨线程调用的检查。

 //取消跨线程的访问
Control.CheckForIllegalCrossThreadCalls = false;

使用上述的方法虽然可以保证程序正常运行并实现应用的功能,但是在实际的软件开发中,做如此设置是不安全的(不符合.NET的安全规范),在产品软件的开发中,此类情况是不允许的。如果要在遵守.NET安全标准的前提下,实现从一个线程成功地访问另一个线程创建的空间,要使用C#的方法回调机制。

2.使用回调函数 ( 委托的应用 )

(1) 定义声明回调

(2) 初始化回调

(3) 触发动作

         /// <summary>
/// 定义委托
/// </summary>
/// <param name="str"></param>
private delegate void SetValueDelegate(string str); /// <summary>
/// 声明委托
/// </summary>
SetValueDelegate test; /// <summary>
/// 回调方法
/// </summary>
/// <param name="str"></param>
private void SetTbValue(string str)
{
this.textBox1.Text = str;
} private void button1_Click(object sender, EventArgs e)
{
// 实例化委托
test = new SetValueDelegate(SetTbValue); Action<string> action = new Action<string>((str) =>
{
// 解决跨线程赋值
this.textBox1.Invoke(test, str);
}); action.BeginInvoke("Test", null, null);
}

我个人比较使用喜欢使用事件解决(原理同委托一样),但省掉不少代码

         private void button1_Click(object sender, EventArgs e)
{
Action<string> action = new Action<string>((str) =>
{
// 解决跨线程赋值
this.textBox1.Invoke(
new Action<string>((param) =>
{
this.textBox1.Text = param;
}
), str);
}); action.BeginInvoke("Test", null, null);
}