转载连接: http://blog.youkuaiyun.com/eyuanatqqdotcom/article/details/7560787
如何:对 Windows 窗体控件进行线程安全调用
使用多线程提高 Windows 窗体应用程序的性能时,必须注意以线程安全方式调用控件。
示例
访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。
.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”
此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。
下面的代码示例演示如何从辅助线程以线程安全方式和非线程安全方式调用 Windows 窗体控件。它演示一种以非线程安全方式设置 TextBox 控件的 Text 属性的方法,还演示两种以线程安全方式设置 Text 属性的方法。
代码:
Form1:
Form1.Designer:
- namespace CrossThreadDemo
- {
- partial class Form1
- {
-
-
-
- private System.ComponentModel.IContainer components = null;
-
-
-
-
-
- protected override void Dispose(bool disposing)
- {
- if (disposing && (components != null))
- {
- components.Dispose();
- }
- base.Dispose(disposing);
- }
-
- #region Windows 窗体设计器生成的代码
-
-
-
-
-
- private void InitializeComponent()
- {
- this.textBox1 = new System.Windows.Forms.TextBox();
- this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
- this.setTextUnsafeBtn = new System.Windows.Forms.Button();
- this.setTextSafeBtn = new System.Windows.Forms.Button();
- this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button();
- this.SuspendLayout();
-
-
-
- this.textBox1.Location = new System.Drawing.Point(18, 13);
- this.textBox1.Name = "textBox1";
- this.textBox1.Size = new System.Drawing.Size(251, 21);
- this.textBox1.TabIndex = 0;
-
-
-
- this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
-
-
-
- this.setTextUnsafeBtn.Location = new System.Drawing.Point(16, 54);
- this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
- this.setTextUnsafeBtn.Size = new System.Drawing.Size(75, 23);
- this.setTextUnsafeBtn.TabIndex = 1;
- this.setTextUnsafeBtn.Text = "非线程安全";
- this.setTextUnsafeBtn.UseVisualStyleBackColor = true;
- this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
-
-
-
- this.setTextSafeBtn.Location = new System.Drawing.Point(106, 54);
- this.setTextSafeBtn.Name = "setTextSafeBtn";
- this.setTextSafeBtn.Size = new System.Drawing.Size(75, 23);
- this.setTextSafeBtn.TabIndex = 2;
- this.setTextSafeBtn.Text = "线程安全";
- this.setTextSafeBtn.UseVisualStyleBackColor = true;
- this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
-
-
-
- this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(198, 54);
- this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
- this.setTextBackgroundWorkerBtn.Size = new System.Drawing.Size(75, 23);
- this.setTextBackgroundWorkerBtn.TabIndex = 3;
- this.setTextBackgroundWorkerBtn.Text = "后台Worker";
- this.setTextBackgroundWorkerBtn.UseVisualStyleBackColor = true;
- this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click);
-
-
-
- this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 12F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
- this.ClientSize = new System.Drawing.Size(290, 90);
- this.Controls.Add(this.setTextBackgroundWorkerBtn);
- this.Controls.Add(this.setTextSafeBtn);
- this.Controls.Add(this.setTextUnsafeBtn);
- this.Controls.Add(this.textBox1);
- this.Name = "Form1";
- this.Text = "Form1";
- this.Load += new System.EventHandler(this.Form1_Load);
- this.ResumeLayout(false);
- this.PerformLayout();
-
- }
-
- #endregion
-
- private System.Windows.Forms.TextBox textBox1;
- private System.ComponentModel.BackgroundWorker backgroundWorker1;
- private System.Windows.Forms.Button setTextUnsafeBtn;
- private System.Windows.Forms.Button setTextSafeBtn;
- private System.Windows.Forms.Button setTextBackgroundWorkerBtn;
- }
- }
步骤总结:
对 Windows 窗体控件的非线程安全调用
对 Windows 窗体控件的非线程安全调用方式是从辅助线程直接调用。调用应用程序时,调试器会引发一个InvalidOperationException,警告对控件的调用不是线程安全的。
(代码略,上边代码的非线程安全部分)
对 Windows 窗体控件的线程安全调用
对 Windows 窗体控件进行线程安全调用
-
查询控件的 InvokeRequired 属性。
-
如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke。
-
如果 InvokeRequired 返回 false,则直接调用控件。
在下面的代码示例中,此逻辑是在一个称为 SetText 的实用工具方法中实现的。名为 SetTextDelegate 的委托类型封装SetText 方法。TextBox 控件的 InvokeRequired 返回true 时,SetText 方法创建 SetTextDelegate 的一个实例,并调用窗体的Invoke 方法。这使得 SetText 方法被创建 TextBox 控件的线程调用,而且在此线程上下文中将直接设置Text属性。
(代码略,上边代码的线程安全部分)
使用 BackgroundWorker 进行的线程安全调用
(代码略,上边代码的后台工作线程部分)
备注(不敢谎称译注):
该文章原文本是简体中文,但作为程序员,大家都明白,文字说明并不能让你了解多少内容,实例代码才是你能够学习到知识的地方,这也就是很多人的博客里简单的说上几句介绍,然后说“看代码”。对于读者来说,光看文字说明是看不懂的,对于作者来说,光写文字说明也是写不出想法的。只有把代码和说明放在一块,才方便读写,所有多数人在代码里写很多注释来说明,而不是在代码外写大段的注释。
这篇文字原文是中文,但是代码的注释却都是英文,因此我尝试着把英文注释翻译为中文,在转载和翻译上犹豫了一会,最后无耻的选择了翻译。。。
我没有什么本事,去翻译一篇好文章,只能自己硬着头皮读一些不得不读的E文文章或者代码,这次的情况也是这样,在自己将就着读下来以后,想把它汉化后备份下来,等到哪天需要的时候,再翻出来参考下。但放在自己的机子上并不是一个好的方法,东西乱糟糟的,过一段时间就找不到了,放在网上,自己容易找到,还可以让百度、谷歌帮忙查找。
如果你链接到了这篇文章,然后找到了你想要的东西,那我很高兴,如果耽误了你的时间,我只能说句对不起了。
QQ:373048914,希望和众多的菜鸟共同进步。