C# 对 Windows 窗体控件进行线程安全调用

35 篇文章 0 订阅
30 篇文章 0 订阅

参考自:http://msdn.microsoft.com/ZH-CN/library/SYSTEM.WINDOWS.FORMS.CONTROL.INVOKE.aspx

如果使用多线程来提高 Windows 窗体应用程序的性能,则必须确保以线程安全方式调用控件

访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能会出现其他与线程相关的 Bug,例如争用情况和死锁。

确保以线程安全方式访问控件非常重要。

在未使用 Invoke 方法的情况下,从不是创建某个控件的线程的其他线程调用该控件是不安全的。 

.NET Framework 可帮助检测”以非线程安全方式访问控件“这一问题。在调试器中运行应用程序时,如果一个不是创建某个控件的线程的其他线程调用该控件,则调试器会引发一个InvalidOperationException,并显示以下消息:“从不是创建控件‘控件名称’的线程访问它。”

方法一:Control.Invoke()方法

同一个类中比较简单,直接用委托就可以实现,步骤如下:

(1) 定义一个委托,或使用系统提供的委托,如:MethodInvoke

(2) 定义一个符合委托类型的函数,包含具体的对控件的操作;

(3) 在线程的回调函数中,定义委托对象,然后用Control.Invoke()或Control.BeginInvoke()进行UI委托操作

简单示例:

举个简单例子说明下使用方法,比如你在启动一个线程,在线程的方法中想更新窗体中的一个TextBox

//定义一个委托
public delegate void MyInvoke(string,string);

//线程回调函数
public void DoWork()
{
	//委托对象
	MyInvoke mi = new MyInvoke(UpdateForm);
	//异步调用委托
	this.BeginInvoke(mi, new Object[] {"我是文本框","haha"});
}

//符合委托的函数,进行TextBox的操作
public void UpdateForm(string param1,string parm2)
{
	this.textBox1.Text = param1+parm2;
}
不同类中则要借助事件来实现,步骤如下:

(1) 事件触发者类:

1> 定义一个委托;

2> 定义一个该委托类型的事件;

3> 触发事件;

(2) 事件响应者类:

1> 定义一个函数,用来处理事件响应;

2> 订阅事件,并为该事件添加响应;

简单示例:

在多线程中接收消息,然后在ListBox中进行显示

在事件触发类中,定义“MsgEvent”事件;

在事件响应类中,订阅该事件,并在ListBox中进行显示;

事件触发类代码如下:

public class MsgRecver
{
	//创建一个委托,返回类型为void,两个参数  
	public delegate void MsgEventHandle(object sender, MsgEventArgs e);  
  
	//将创建的委托和特定事件关联,在这里特定的事件为MsgEvent
	public event MsgEventHandle MsgEvent;  

	// 触发事件  
	MsgEvent(this, Args);
}
事件响应类代码如下:

public class MsgHandle 
{  
    //注意,构造函数中传入事件发生类的对象,只是为了订阅事件用  
    public EventResponse(MsgRecver msgRecver)  
    {  
        //产生一个委托实例并添加到MsgRecver产生的事件响应列表中  
        msgRecver.msgEventHandle += new msgRecver.MsgEventHandle(Echo);  
    }

    private void Echo(object sender, MsgEventArgs e)  
    {  
        // 真正的事件处理函数  
        this.listBox1.Text+=e.Message;  
    }
}

  • 方法二:使用 BackgroundWorker 进行线程安全调用

     在应用程序中实现多线程的首选方式是使用BackgroundWorker 组件。 BackgroundWorker 组件使用事件驱动模型实现多线程

     后台线程运行 DoWork 事件处理程序,而创建控件的线程运行 ProgressChanged 和 RunWorkerCompleted 事件处理程序。 

    可以从 ProgressChanged 和 RunWorkerCompleted 事件处理程序调用控件。

    步骤如下:

    (1) 创建一个方法,该方法用于执行您希望在后台线程中完成的工作。 

    注意:不要调用由此方法中的主线程创建的控件。

    (2) 创建一个方法,用于在后台工作完成后报告结果。可以调用由此方法中的主线程创建的控件。

    (3) 将步骤 1 中创建的方法绑定到 BackgroundWorker 的实例的 DoWork 事件

    (4) 将步骤 2 中创建的方法绑定到同一实例的 RunWorkerCompleted 事件。

    (5) 若要启动后台线程,请调用 BackgroundWorker 实例的 RunWorkerAsync() 方法。




  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值