浅谈C#跨线程调用窗体控件(比如TextBox)引发的线程安全问题

如何:对 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 窗体控件进行线程安全调用

查询控件的 InvokeRequired 属性。

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

如果 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(12, 12);
   this.textBox1.Name = "textBox1";
   this.textBox1.Size = new System.Drawing.Size(240, 20);
   this.textBox1.TabIndex = 0;
   //
   // setTextUnsafeBtn
   //
   this.setTextUnsafeBtn.Location = new System.Drawing.Point(15, 55);
   this.setTextUnsafeBtn.Name = "setTextUnsafeBtn";
   this.setTextUnsafeBtn.TabIndex = 1;
   this.setTextUnsafeBtn.Text = "Unsafe Call";
   this.setTextUnsafeBtn.Click += new System.EventHandler(this.setTextUnsafeBtn_Click);
   //
   // setTextSafeBtn
   //
   this.setTextSafeBtn.Location = new System.Drawing.Point(96, 55);
   this.setTextSafeBtn.Name = "setTextSafeBtn";
   this.setTextSafeBtn.TabIndex = 2;
   this.setTextSafeBtn.Text = "Safe Call";
   this.setTextSafeBtn.Click += new System.EventHandler(this.setTextSafeBtn_Click);
   //
   // setTextBackgroundWorkerBtn
   //
   this.setTextBackgroundWorkerBtn.Location = new System.Drawing.Point(177, 55);
   this.setTextBackgroundWorkerBtn.Name = "setTextBackgroundWorkerBtn";
   this.setTextBackgroundWorkerBtn.TabIndex = 3;
   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(268, 96);
   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)引发的线程安全问题就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持我们。

(0)

相关推荐

  • C#之WinForm跨线程访问控件实例

    本文实例讲述了C#中WinForm跨线程访问控件的实现方法,分享给大家供大家参考. 具体实现方法如下: 1.跨线程访问控件委托和类的定义 复制代码 代码如下: using System; using System.Windows.Forms; namespace ahwildlife.Utils {     /// <summary>     /// 跨线程访问控件的委托     /// </summary>     public delegate void InvokeDeleg

  • 在多线程中调用winform窗体控件的实现方法

    本文实例讲述了在C#中实现多线程中调用winform窗体控件的方法,对于C#程序设计的学习有着很好的借鉴参考价值.具体方法如下: 首先,由于Windows窗体控件本质上不是线程安全的.因此如果有两个或多个线程适度操作某一控件的状态(set value),则可能会迫使该控件进入一种不一致的状态.还可能出现其他与线程相关的 bug,包括争用和死锁的情况.于是在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationExceptio

  • C#实现跨线程操作控件方法

    本文实例讲述了C#实现跨线程操作控件方法,分享给大家供大家参考.具体实现方法如下: 由于在.net平台下Winform.wpf禁止跨线程直接访问控件,因此在必须跨线程访问控件的时候采用异步方式. 1.winform项目中跨线程访问控件: 编写一个Winform小实例:在做winform项目中,有时为了将系统运行的状态实时显示到Form中,因此添加一个RichTextbox控件实时显示系统运行日志.本例实现的操作是将日志以字符串的形式写入RichTextbox控件,因为是实时显示,所以涉及到跨线程

  • C#多线程与跨线程访问界面控件的方法

    本文实例讲述了C#多线程与跨线程访问界面控件的方法.分享给大家供大家参考.具体分析如下: 在编写WinForm访问WebService时,常会遇到因为网络延迟造成界面卡死的现象.启用新线程去访问WebService是一个可行的方法. 典型的,有下面的启动新线程示例: 复制代码 代码如下: private void LoadRemoteAppVersion()  {      if (FileName.Text.Trim() == "") return;      StatusLabel

  • 浅谈C#跨线程调用窗体控件(比如TextBox)引发的线程安全问题

    如何:对 Windows 窗体控件进行线程安全调用 访问 Windows 窗体控件本质上不是线程安全的. 如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态. 还可能会出现其他与线程相关的 Bug,例如争用情况和死锁. 确保以线程安全方式访问控件非常重要. 在未使用 Invoke 方法的情况下,从不是创建某个控件的线程的其他线程调用该控件是不安全的. 以下非线程安全的调用的示例. // This event handler creates a thread that

  • 浅谈layui里的上传控件问题

    很多时候,项目里都是需要上传功能的,现在就来研究研究, 首先,在html页面引入layui的包,如: <link rel="stylesheet" href="map/plug-in/scripts/layui/css/layui.css" rel="external nofollow" /> <script type="text/javascript" src="map/plug-in/scrip

  • 浅谈Android RecyclerView UI的滚动控件示例

    ListView 由于其强大的功能,在过去的 Andorid 开发中使用非常广泛.不过 ListView 需要优化来提升运行效率,就像我们之前所优化的那样,否则性能将很差.还有就是只能够纵向滚动,如果要想实现横向移动,用 ListView 是做不到的. RecyclerView 可以说是一个增强版的 ListView .它不仅实现了和 ListView 同样的效果,而且还优化了 ListView 存在的各种不足. RecyclerView 现在可是官方推荐使用的滚动控件哦O(∩_∩)O~ 1 基

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

    示例 访问 Windows 窗体控件本质上不是线程安全的.如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态.还可能出现其他与线程相关的 bug,包括争用情况和死锁.确保以线程安全方式访问控件非常重要. .NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题.在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:"从不是创建控件 contr

  • 浅谈window.onbeforeunload() 事件调用ajax

    经常有这样的需求,就是在离开某个web页面时,用户不一定点注销,这样会导致会话不能及时销毁.为实现用户离开页面时,自动注销功能,需要在web页面的onbeforeunload事件处理函数中发送注销命令.这个地方大多用Ajax实现.有时还涉及到跨域访问的问题.这个地方就存在浏览器的兼容性问题. 浏览器在处理这个需求时的不兼容性有如下两点: 1.处理Ajax时的不兼容性,这里使用jQuery来解决. 2.在发送Ajax请求时的不兼容性 主要代码如下: function logout() { var

  • C#子线程更新UI控件的方法实例总结

    本文实例总结了C#子线程更新UI控件的方法,对于桌面应用程序设计的UI界面控制来说非常有实用价值.分享给大家供大家参考之用.具体分析如下: 一般在winform C/S程序中经常会在子线程中更新控件的情况,桌面程序UI线程是主线程,当试图从子线程直接修改控件属性时会出现"从不是创建控件的线程访问它"的异常提示. 跨线程更新UI控件的常用方法有两种: 1.使用控件自身的invoke/BeginInvoke方法 2.使用SynchronizationContext的Post/Send方法更

  • c#后台线程访问前台控件并显示信息示例

    复制代码 代码如下: //设置为后台线程 Thread th = new Thread(delegate() { append();});th.IsBackground = true;th.Start(); //在append方法里面需要调用前台控件 public void append(){ // ... 业务处理 this.Invoke(new flushMessage(showMessage), new object[] { row["Code"].ToString(), res

  • 基于C#调用OCX控件的常用方法(推荐)

    小伙伴们在使用ICP提供的各种能力进行集成开发时常常会遇到一些技术上的困扰,例如ICP中很多接口是通过OCX控件的方式提供的,如何调用这些接口,就成了一个不大不小的问题,毕竟开发指南上可没这些内容啊~别着急,今天我就给大家介绍一下C#中调用OCX接口的常用方法.^_^y 开发环境:win7企业版,vs2010 控件:以voice.ocx为例 一.Winform工程中调用OCX控件 1.使用regsvr32控制台命令注册控件: 1.1 打开控制台,进入控件存放路径 1.2 输入控制台命令:regs

随机推荐