目前使用的方式是:

// 后台线程代码块中
Invoke((MethodInvoker)delegate {
// 在UI线程执行的代码块
});

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

http://msdn.microsoft.com/zh-cn/library/system.threading.parameterizedthreadstart(v=vs.80).aspx

http://msdn.microsoft.com/zh-cn/library/ts553s52(v=vs.80).aspx

private void button1_Click(object sender, EventArgs e)
{
Thread thread = new Thread(() =>
{
Debug.WriteLine(textBox1.Text);
Thread.Sleep();
if (textBox1.InvokeRequired)
{
this.textBox1.Invoke(new Action<string>(s =>
{
this.textBox1.Text = s;
}),
DateTime.Now.ToString());
}
else
{
this.textBox1.Text = DateTime.Now.ToString();
} });
thread.IsBackground = true;
thread.Start();
}

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

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

此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。

说明:

可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。

下面的代码示例演示如何从辅助线程以线程安全方式和非线程安全方式调用 Windows 窗体控件。它演示一种以非线程安全方式设置 TextBox 控件的 Text 属性的方法,还演示两种以线程安全方式设置 Text 属性的方法。

 
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());
} }
}

对 Windows 窗体控件的非线程安全调用

对 Windows 窗体控件的非线程安全调用方式是从辅助线程直接调用。调用应用程序时,调试器会引发一个InvalidOperationException,警告对控件的调用不是线程安全的。

 
		// 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.";
}

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

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

  1. 查询控件的 InvokeRequired 属性。

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

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

在下面的代码示例中,此逻辑是在一个称为 SetText 的实用工具方法中实现的。名为 SetTextDelegate 的委托类型封装 SetText方法。TextBox 控件的 InvokeRequired 返回 true 时,SetText 方法创建 SetTextDelegate 的一个实例,并调用窗体的 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.");
}
 
		// 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;
}
}

使用 BackgroundWorker 进行的线程安全调用

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

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

 
		// 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.";
}

Windows 窗体上的 ActiveX 控件

如果在窗体上使用 ActiveX 控件,则在调试器下运行时可能会收到线程间 InvalidOperationException。发生这种情况时,ActiveX 控件不支持多线程处理。有关使用 Windows 窗体的 ActiveX 控件的更多信息,请参见 Windows 窗体和非托管应用程序

如果您使用的是 Visual Studio,则可以通过禁用 Visual Studio 宿主进程来防止此异常发生。

如何:禁用宿主进程
如何:禁用宿主进程
如何:禁用承载进程
如何:禁用承载进程
如何:禁用承载进程

带参数的多线程之委托

using System;
using System.Threading; // The ThreadWithState class contains the information needed for
// a task, and the method that executes the task.
//
public class ThreadWithState {
// State information used in the task.
private string boilerplate;
private int value; // The constructor obtains the state information.
public ThreadWithState(string text, int number)
{
boilerplate = text;
value = number;
} // The thread procedure performs the task, such as formatting
// and printing a document.
public void ThreadProc()
{
Console.WriteLine(boilerplate, value);
}
} // Entry point for the example.
//
public class Example {
public static void Main()
{
// Supply the state information required by the task.
ThreadWithState tws = new ThreadWithState(
"This report displays the number {0}.", 42); // Create a thread to execute the task, and then
// start the thread.
Thread t = new Thread(new ThreadStart(tws.ThreadProc));
t.Start();
Console.WriteLine("Main thread does some work, then waits.");
t.Join();
Console.WriteLine(
"Independent task has completed; main thread ends.");
}
} // 带回调的委托
using System;
using System.Threading; // The ThreadWithState class contains the information needed for
// a task, the method that executes the task, and a delegate
// to call when the task is complete.
//
public class ThreadWithState {
// State information used in the task.
private string boilerplate;
private int value; // Delegate used to execute the callback method when the
// task is complete.
private ExampleCallback callback; // The constructor obtains the state information and the
// callback delegate.
public ThreadWithState(string text, int number,
ExampleCallback callbackDelegate)
{
boilerplate = text;
value = number;
callback = callbackDelegate;
} // The thread procedure performs the task, such as
// formatting and printing a document, and then invokes
// the callback delegate with the number of lines printed.
public void ThreadProc()
{
Console.WriteLine(boilerplate, value);
if (callback != null)
callback(1);
}
} // Delegate that defines the signature for the callback method.
//
public delegate void ExampleCallback(int lineCount); // Entry point for the example.
//
public class Example
{
public static void Main()
{
// Supply the state information required by the task.
ThreadWithState tws = new ThreadWithState(
"This report displays the number {0}.",
42,
new ExampleCallback(ResultCallback)
); Thread t = new Thread(new ThreadStart(tws.ThreadProc));
t.Start();
Console.WriteLine("Main thread does some work, then waits.");
t.Join();
Console.WriteLine(
"Independent task has completed; main thread ends.");
} // The callback method must match the signature of the
// callback delegate.
//
public static void ResultCallback(int lineCount)
{
Console.WriteLine(
"Independent task printed {0} lines.", lineCount);
}
}

如何:对 Windows 窗体控件进行线程安全调用的更多相关文章

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

    //主线程 public delegate void UpdateMessage(string mes); public void UpdatePortMessage(string mes) { th ...

  2. c#中跨线程调用windows窗体控件

    c#中跨线程调用windows窗体控件解决. 我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首先来 ...

  3. c#中如何跨线程调用windows窗体控件

    c#中如何跨线程调用windows窗体控件?   我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首 ...

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

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

  5. 用于列出选项的Windows窗体控件

    可以提供选项列表的控件有ListBox.ComboBox.CheckedListBox,如何正确的使用和选择这些控件,下面对此进行讨论.首先对这三种控件的功能分别进行说明: ListBox ListB ...

  6. c#中如何跨线程调用windows窗体控件?

    我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首先来看传统方法: public partial c ...

  7. Windows窗体控件实现内容拖放(DragDrop)功能

    一.将控件内容拖到其他控件 在开发过程中,经常会有这样的要求,拖动一个控件的数据到另外一个控件中.例如将其中一个ListBox中的数据拖到另一个ListBox中.或者将DataGridView中的数据 ...

  8. vs2017 C# ActiveX浏览器插件 创建 发布 C# windows窗体控件库(.NET Framework)注意事项

    vs2017需要安装插 插件下载地址:https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.Micro ...

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

    如何:对 Windows 窗体控件进行线程安全调用 访问 Windows 窗体控件本质上不是线程安全的. 如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态. 还可能会出现 ...

随机推荐

  1. Nopcommerce 二次开发0

    Nopcommerce  是国外的一个高质量的开源b2c网站系统,基于EntityFramework6.0和MVC5.0,使用Razor模板引擎,有很强的插件机制,包括支付配送功能都是通过插件来实现的 ...

  2. 剑指Offer:解决难题时的三大方法

    1.画图 让抽象的东西变得直观生动起来.比如设计二叉树,链表,栈,队列这些数据结构时. 2.举例子 同样可以化抽象为直观.能够更清晰的展现思路.从例子归纳出一般做法. 3.分解 有时问题本身是比较复杂 ...

  3. Raspberry pi(-) Mac下安装系统

    购买树莓派一般的操作说明书都是windows下的,因为本人只有MBP,外加虚拟机Parallels Desktop无法识别SD卡,所以就直接参考Mac教程了. 1.系统镜像官网下载 推荐用Raspbi ...

  4. pthon在Notepad++中执行方式

    使用 Notepad++ 编辑运行 Python 程序         Notepad++是一个开源的文本编辑器,功能强大而且使用方便.编辑和调试 Python 程序使用什么编辑器或者 IDE不同人有 ...

  5. 【SSM 4】Mybatis逆向生成工具

    在上一篇博客中说到,Mybatis是灵活的SQL语句应用,不想Hibernate一样有其封装好的方法,那么,当我们用Mybatis的时候(Hibernate),我们都需要编写其实体类,和配置文件.本篇 ...

  6. Linux学习笔记2_mysql安装

    查看文件内容的命令有很多:cat, tac, more, less, head, tail, nl. cat由第一行开始显示档案内容:tac从最后一行开始显示,可以看出tac是cat的倒着写:more ...

  7. 使用Git命令上传本地项目

    前提,安装git,使用cmd进入项目根目录. 初始化git init 再添加文件git add .git commit -m '项目名' 上传项目到Github仓库git remote add ori ...

  8. LeetCode-179. Largest Number

    179. Largest Number Given a list of non negative integers, arrange them such that they form the larg ...

  9. python容器类型:列表,字典,集合等

    容器的概念我是从C++的STL中学到的 什么是容器? 容器是用来存储和组织其他对象的对象. 也就是说容器里面可以放很多东西,这些东西可以是字符串,可以是整数,可以是自定义类型,然后把这些东西有组织的存 ...

  10. WPS for Linux(ubuntu)字体缺失解决办法(转)

    原文:http://www.cnblogs.com/liangml/p/5969404.html 启动WPS for Linux后,出现提示"系统缺失字体" . 出现提示的原因是因 ...