经验分享-线程间操作无效:从不是创建控件的线程访问它

使用多线程提高 Windows 窗体应用程序的性能时,必须注意以线程安全方式调用控件。

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

比如在一个按钮中执行复杂操作,并将复杂操作最后返回的值加入一个ListView或ComboBox中候选。这个时候程序会卡,当程序员将这些卡代码放进线程(Thread)中后发现当对控件操作时出现“线程间操作无效: 从不是创建控件的线程访问它”异常。

为什么.net不让我们跨线程操作控件,这是有好处的。因为如果你的线程多了,那么当两个线程同时尝试将一个控件变为自己需要的状态时,线程的死锁就会发生。

三种解决方法。

1、在窗体加载事件中把CheckForIllegalCrossThreadCalls设置为false。

CheckForIllegalCrossThreadCalls在.net1.x中默认是false,也就是不检查,.net2.0和3.x默认是true。说明这是微软有意的引导,说不定以后不让你改了。这也是很多1.x用户在刚用2.0时不习惯跨线程更新ui的原因之一。

2、委托

3、使用 BackgroundWorker控件

在应用程序中实现多线程的首选方式是使用 BackgroundWorker 组件。BackgroundWorker 组件使用事件驱动模型实现多线程。辅助线程运行 DoWork 事件处理程序,创建控件的线程运行ProgressChanged 和 RunWorkerCompleted 事件处理程序。注意不要从 DoWork 事件处理程序调用您的任何控件。

下面的代码示例不异步执行任何工作,因此没有 DoWork 事件处理程序的实现。TextBox 控件的Text 属性在 RunWorkerCompleted 事件处理程序中直接设置。

需要异步时,把获取数据的代码写在BackgroundWork控件的DoWork事件中并把获取到的数据赋值给e.Result,然后在RunWorkerCompleted事件中接收e.Result并赋值给控件。

参考资料:http://msdn.microsoft.com/zh-cn/visualc/ms171728(VS.85,printer).aspx

 

俊霖

发表评论

您必须