如何:对 Windows 窗体控件进行线程安全调用

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

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

        // This event handler creates a thread that calls a
// Windows Forms control in an unsafe way.
private void setTextUnsafeBtn_Click(
object sender,
EventArgs e)
{
this.demoThread =
new Thread(new ThreadStart(this.ThreadProcUnsafe));
this.demoThread.Start();
}
// This method is executed on the worker thread and makes
// an unsafe call on the TextBox control.
private void ThreadProcUnsafe()
{
this.textBox1.Text = "This text was set unsafely.";
}

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

此异常在调试期间和运行时的某些情况下可靠地发生。 在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。 我们强烈建议您在发现此问题时进行修复,但您可以通过将 CheckForIllegalCrossThreadCalls 属性设置为 false 来禁用它。(不推荐)


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

  1. 查询控件的 InvokeRequired 属性。

  2. 如果 InvokeRequired 返回 true,则使用实际调用控件的委托来调用 Invoke

  3. 如果 InvokeRequired 返回 false,则直接调用控件。

  在下面的代码示例中,将在由后台线程执行的 ThreadProcSafe 方法中实现线程安全调用。 如果 TextBox 控件的 InvokeRequired 返回 true,则 ThreadProcSafe 方法会创建 SetTextCallback 的一个实例,并将该实例传递给窗体的 Invoke 方法。 这使得 SetText 方法被创建 TextBox 控件的线程调用,而且在此线程上下文中将直接设置 Text 属性。

       // This event handler creates a thread that calls a
// Windows Forms control in a thread-safe way.
private void setTextSafeBtn_Click(
object sender,
EventArgs e)
{
this.demoThread =
new Thread(new ThreadStart(this.ThreadProcSafe));
this.demoThread.Start();
} // This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{
this.SetText("This text was set safely.");
}
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms; namespace CrossThreadDemo
{
public class Form1 : Form
{
// This delegate enables asynchronous calls for setting
// the text property on a TextBox control.
delegate void SetTextCallback(string text); // This thread is used to demonstrate both thread-safe and
// unsafe ways to call a Windows Forms control.
private Thread demoThread = null; // This BackgroundWorker is used to demonstrate the
// preferred way of performing asynchronous operations.
private BackgroundWorker backgroundWorker1; private TextBox textBox1;
private Button setTextUnsafeBtn;
private Button setTextSafeBtn;
private Button setTextBackgroundWorkerBtn; private System.ComponentModel.IContainer components = null; public Form1()
{
InitializeComponent();
} protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
} // This event handler creates a thread that calls a
// Windows Forms control in an unsafe way.
private void setTextUnsafeBtn_Click(
object sender,
EventArgs e)
{
this.demoThread =
new Thread(new ThreadStart(this.ThreadProcUnsafe)); this.demoThread.Start();
} // This method is executed on the worker thread and makes
// an unsafe call on the TextBox control.
private void ThreadProcUnsafe()
{
this.textBox1.Text = "This text was set unsafely.";
} // This event handler creates a thread that calls a
// Windows Forms control in a thread-safe way.
private void setTextSafeBtn_Click(
object sender,
EventArgs e)
{
this.demoThread =
new Thread(new ThreadStart(this.ThreadProcSafe)); this.demoThread.Start();
} // This method is executed on the worker thread and makes
// a thread-safe call on the TextBox control.
private void ThreadProcSafe()
{
this.SetText("This text was set safely.");
} // This method demonstrates a pattern for making thread-safe
// calls on a Windows Forms control.
//
// If the calling thread is different from the thread that
// created the TextBox control, this method creates a
// SetTextCallback and calls itself asynchronously using the
// Invoke method.
//
// If the calling thread is the same as the thread that created
// the TextBox control, the Text property is set directly. private void SetText(string text)
{
// InvokeRequired required compares the thread ID of the
// calling thread to the thread ID of the creating thread.
// If these threads are different, it returns true.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
} // This event handler starts the form's
// BackgroundWorker by calling RunWorkerAsync.
//
// The Text property of the TextBox control is set
// when the BackgroundWorker raises the RunWorkerCompleted
// event.
private void setTextBackgroundWorkerBtn_Click(
object sender,
EventArgs e)
{
this.backgroundWorker1.RunWorkerAsync();
} // This event handler sets the Text property of the TextBox
// control. It is called on the thread that created the
// TextBox control, so the call is thread-safe.
//
// BackgroundWorker is the preferred way to perform asynchronous
// operations. private void backgroundWorker1_RunWorkerCompleted(
object sender,
RunWorkerCompletedEventArgs e)
{
this.textBox1.Text =
"This text was set safely by BackgroundWorker.";
} #region Windows Form Designer generated code private void InitializeComponent()
{
this.textBox1 = new System.Windows.Forms.TextBox();
this.setTextUnsafeBtn = new System.Windows.Forms.Button();
this.setTextSafeBtn = new System.Windows.Forms.Button();
this.setTextBackgroundWorkerBtn = new System.Windows.Forms.Button();
this.backgroundWorker1 = new System.ComponentModel.BackgroundWorker();
this.SuspendLayout();
//
// textBox1
//
this.textBox1.Location = new System.Drawing.Point(, );
this.textBox1.Name = "textBox1";
this.textBox1.Size = new System.Drawing.Size(, );
this.textBox1.TabIndex = ;
//
// setTextUnsafeBtn
//
this.setTextUnsafeBtn.Location = new System.Drawing.Point(, );
this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
this.setTextUnsafeBtn.TabIndex = ;
this.setTextUnsafeBtn.Text = "Unsafe Call";
this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
//
// setTextSafeBtn
//
this.setTextSafeBtn.Location = new System.Drawing.Point(, );
this.setTextSafeBtn.Name = "setTextSafeBtn";
this.setTextSafeBtn.TabIndex = ;
this.setTextSafeBtn.Text = "Safe Call";
this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
//
// setTextBackgroundWorkerBtn
//
this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(, );
this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
this.setTextBackgroundWorkerBtn.TabIndex = ;
this.setTextBackgroundWorkerBtn.Text = "Safe BW Call";
this.setTextBackgroundWorkerBtn.Click += new System.EventHandler(this.setTextBackgroundWorkerBtn_Click);
//
// backgroundWorker1
//
this.backgroundWorker1.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.backgroundWorker1_RunWorkerCompleted);
//
// Form1
//
this.ClientSize = new System.Drawing.Size(, );
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.ResumeLayout(false);
this.PerformLayout(); } #endregion [STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form1());
} }
}

C#跨线程调用窗体控件(比如TextBox)引发的线程安全问题的更多相关文章

  1. Visual studio之C#跨线程调用UI控件

    背景 当前串口通讯项目,多个线程需要同时利用richTextBoxMsg控件打印信息,直接调用会造成线程不安全,严重的时候会直接导致UI线程挂掉,因此本篇就跨线程调用UI控件做个记录. 正文 定义控件 ...

  2. C# 跨线程调用form控件技巧及byte[]与string型相互转换

    跨线程调用form控件技巧 private delegate void MethodSocket(object obj);//使用托管 ss = "OK"; this.BeginI ...

  3. Atitit.swt 线程调用ui控件的方法

    Atitit.swt 线程调用ui控件的方法 1 SwingUtilities.invokeLater1 2 display.asyncExec方法1 3  display.timerExec(500 ...

  4. C# winform 跨线程更改窗体控件的属性

    当winform程序中新开一个线程,是无法改变主线程中窗体控件的属性的,否则运行时会报错. 若想在其他线程中控制主线程中的窗体控件,则必须利用BeginInvoke方法. 例如:添加一个名为textb ...

  5. c#.net WinForm 线程内 调用窗体控件

    richTextBox1.BeginInvoke(new EventHandler(delegate { richTextBox1.AppendText("正在提交服务器..\r\n&quo ...

  6. 如何跨线程调用Windows窗体控件

    通过一个子线程来操作主线程中的控件,但是,这样作会出现一个问题(如图1所示),就是TextBox控件是在主线程中创建的,在子线程中并没有对其进行创建,也就是从不是创建控件的线程访问它.那么,如何解决跨 ...

  7. 如何:对 Windows 窗体控件进行线程安全调用

    http://msdn.microsoft.com/zh-cn/library/ms171728(VS.90).aspx http://msdn.microsoft.com/zh-cn/library ...

  8. 如何在多线程中调用winform窗体控件

    由于 Windows 窗体控件本质上不是线程安全的.因此如果有两个或多个线程适度操作某一控件的状态(set value),则可能会迫使该控件进入一种不一致的状态.还可能出现其他与线程相关的 bug,包 ...

  9. C# 静态函数调用窗体控件

    回调函数方法是静态函数,需要调用窗体控件,赋值或取值. 定义 public static Form1 mainFrm;   mainFrm = this; public partial class F ...

随机推荐

  1. Linux系统编程:socket网络编程(操作篇)

    一.问题思考 问1.网络通信应用在什么场合?通信的前提是什么? 答1.主要应用在不同主机进程间的互相通信,同一主机的进程也可以使用网络进行通信.通信的前提是如何标识通信进程的唯一,由于不同主机的进程极 ...

  2. 测试Servlet生命周期例子程序

    写一个类TestLifeCycleServlet,生成构造器TestLifeCycleServlet();重写HttpServlet的doGet();重写GenericServlet的destroy( ...

  3. Web App、Hybrid App、 Native App

    1.特点: 1. 偏交互的Native,偏浏览的Web:交互指复杂操作,输入/选择什么的2. 已稳定的Native,试错中的Web:H5页面用来做低成本验证很好3. 访问硬件Native,信息展示We ...

  4. HUE配置文件hue.ini 的pig模块详解(图文详解)(分HA集群和非HA集群)

    不多说,直接上干货! 一.默认的pig配置文件 ########################################################################### ...

  5. java设计模式 --------单利模式

    解法一:只适合单线程环境(不好) package test; /** * @author xiaoping * */ public class Singleton { private static S ...

  6. leetcode5:subsets问题

    问题描述: Given a set of distinct integers, nums, return all possible subsets. Note: Elements in a subse ...

  7. System.Transactions事务超时设置

    System.Transactions 有2个超时属性(timeout 与 maxTimeout),可以通过配置文件来进行设置. 1. timeout System.Transactions 默认的t ...

  8. MongoDB 从入门到精通

    1,安装并启动数据库       从官网(www.mongodb.org/downloads)下载一个适合你平台的版本,我的系统是win7 64位的,下载文件也就10几M,将下载的文件解压放到任何目录 ...

  9. Java - 用静态工厂方法代替构造器

    Effective Item - 考虑用静态工厂方法代替构造器我们有两种常见的方法获得一个类的实例: 公有的构造器 提供静态工厂方法(static factory method) 相对公有的构造器,静 ...

  10. git pull和git pull --rebase的使用

    使用下面的关系区别这两个操作: git pull = git fetch + git merge git pull --rebase = git fetch + git rebase 现在来看看git ...