哦,后来才发现这是2005的新特点。。
对 Windows 窗体控件进行线程安全调用
使用多线程提高 Windows 窗体应用程序的性能时,必须注意以线程安全方式调用控件。
示例
访问 Windows 窗体控件本质上不是线程安全的。如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态。还可能出现其他与线程相关的 bug,包括争用情况和死锁。确保以线程安全方式访问控件非常重要。
.NET Framework 有助于在以非线程安全方式访问控件时检测到这一问题。在调试器中运行应用程序时,如果创建某控件的线程之外的其他线程试图调用该控件,则调试器会引发一个 InvalidOperationException,并提示消息:“从不是创建控件 control name 的线程访问它。”
此异常在调试期间和运行时的某些情况下可靠地发生。强烈建议您在显示此错误信息时修复此问题。在调试以 .NET Framework 2.0 版之前的 .NET Framework 编写的应用程序时,可能会出现此异常。
注意
可以通过将 CheckForIllegalCrossThreadCalls 属性的值设置为 false 来禁用此异常。这会使控件以与在 Visual Studio 2003 下相同的方式运行。
下面的代码示例演示如何从辅助线程以线程安全方式和非线程安全方式调用 Windows 窗体控件。它演示一种以非线程安全方式设置 TextBox 控件的 Text 属性的方法,还演示两种以线程安全方式设置 Text 属性的方法。
![code.gif](http://www.kkee.cn/images/code.gif)
using System;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
// http://msdn2.microsoft.com/zh-cn/library/ms171728.aspx#Mtps_DropDownFilterText
namespace CrossThreadDemo
{
public class Form1 : Form
{
//这个代理能异步调用设置文本框
delegate void SetTextCallback(string text);
//这个线程是用来示范2个安全线程和不安全线程调用窗体控建的。
private Thread demoThread = null;
//一个后台工作组件,用它可以进行异步操作
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);
}
//非安全线程触发这个方法。
private void setTextUnsafeBtn_Click(
object sender,
EventArgs e)
{//创建一个线程,调用ThreadProcUnsafe方法
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()
{//安全执行方法一:(1。通过调用一个SetText方法来设定TextBox的文字。)
this.SetText("This text was set safely.");
}
private void SetText(string text)
{
//这个方法使用了InvokeRequired来判断调用是否来自其他线程。
if (this.textBox1.InvokeRequired)//如果来自非主线程,则执行下面
{
SetTextCallback d = new SetTextCallback(SetText);//利用代理,让代理等于这个方法本身。
this.Invoke(d, new object[] { text });//让主窗体线程调用这个方法。
}
else//当主窗体调用此方法时,InvokeRequired为false,则可以在主窗体线程执行修改文本框了。
{
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.
//以下是利用组件BackgroundWorker来进行异步调用。
private void setTextBackgroundWorkerBtn_Click(
object sender,
EventArgs e)
{//当按钮被按下以后,调用组件的RunWorkerAsyne()方法告诉组件:开始执行后台操作。
this.backgroundWorker1.RunWorkerAsync();
//backgroundWorker会在新的线程执行backgroundWorker1.DoWork的内容。
//如果没有DoWork动作,则立刻会回到当前线程执行backgroundWorker1.RunWorkerCompleted
}
// 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());
}
}
}
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;
// http://msdn2.microsoft.com/zh-cn/library/ms171728.aspx#Mtps_DropDownFilterText
namespace CrossThreadDemo
{
public class Form1 : Form
{
//这个代理能异步调用设置文本框
delegate void SetTextCallback(string text);
//这个线程是用来示范2个安全线程和不安全线程调用窗体控建的。
private Thread demoThread = null;
//一个后台工作组件,用它可以进行异步操作
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);
}
//非安全线程触发这个方法。
private void setTextUnsafeBtn_Click(
object sender,
EventArgs e)
{//创建一个线程,调用ThreadProcUnsafe方法
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()
{//安全执行方法一:(1。通过调用一个SetText方法来设定TextBox的文字。)
this.SetText("This text was set safely.");
}
private void SetText(string text)
{
//这个方法使用了InvokeRequired来判断调用是否来自其他线程。
if (this.textBox1.InvokeRequired)//如果来自非主线程,则执行下面
{
SetTextCallback d = new SetTextCallback(SetText);//利用代理,让代理等于这个方法本身。
this.Invoke(d, new object[] { text });//让主窗体线程调用这个方法。
}
else//当主窗体调用此方法时,InvokeRequired为false,则可以在主窗体线程执行修改文本框了。
{
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.
//以下是利用组件BackgroundWorker来进行异步调用。
private void setTextBackgroundWorkerBtn_Click(
object sender,
EventArgs e)
{//当按钮被按下以后,调用组件的RunWorkerAsyne()方法告诉组件:开始执行后台操作。
this.backgroundWorker1.RunWorkerAsync();
//backgroundWorker会在新的线程执行backgroundWorker1.DoWork的内容。
//如果没有DoWork动作,则立刻会回到当前线程执行backgroundWorker1.RunWorkerCompleted
}
// 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());
}
}
}
我另外自己写了个,BackgroundWorker更详细的应用。
Form1.cs
![code.gif](http://www.kkee.cn/images/code.gif)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Threading;
namespace safeThread
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
delegate void del(int i);
Thread ThreadDemo;
private void button1_Click(object sender, EventArgs e)
{
ThreadDemo = new Thread(new ThreadStart (ThreadProSafe));
ThreadDemo.Start();
toolStripStatusLabel2.Text = "运算正在进行中。。";
button1.Text = "运算中..";
this.Cursor = Cursors.WaitCursor;
//this.Dispose(false);
}
private void ThreadProSafe()
{
try
{
for (int i = 1; i <= 500; i++)
{
Console.WriteLine(i);
SetProcess((int)(i / 5));
Thread.Sleep(5);
}
}
finally
{
SetSatus(0);
}
}
private void SetProcess(int vi)
{
if (InvokeRequired)
{
del dd = new del(SetProcess);
this.Invoke(dd, new object[] { vi });
}
else
{
if(vi<=100)
this.progressBar1.Value = vi;
}
//CheckForIllegalCrossThreadCalls
}
private void SetSatus(int vi)
{
if (InvokeRequired)
{
del dd = new del(SetSatus);
this.Invoke(dd, new object[] { vi });
}
else
{
toolStripStatusLabel2.Text = "运算结束!";
button1.Text = "开始运算";
this.Cursor = Cursors.Default;
}
}
private void button2_Click(object sender, EventArgs e)
{
if (((Button)sender).Text == "后台组件运算")
{
this.backgroundWorker1.RunWorkerAsync(10);
this.button2.Text = "取消后台运算";
this.toolStripStatusLabel2.Text = "正在通过组件后台线程运算。。";
this.Cursor = Cursors.WaitCursor;
}
else
{
backgroundWorker1.CancelAsync();
this.toolStripStatusLabel2.Text = "正在取消。。";
}
//MessageBox.Show("OnClick:" + System.AppDomain.GetCurrentThreadId());
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
for (int i = 1; i <= 500; i++)
{
Console.WriteLine(i);
backgroundWorker1.ReportProgress((int)(i / 5));
Thread.Sleep(5);
if (backgroundWorker1.CancellationPending)
{
e.Cancel = true;
break;
}
}
//这里是需要后台运行的程序
}
private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
toolStripStatusLabel2.Text = "运算已经取消!";
}
else
{
toolStripStatusLabel2.Text = "运算结束!";
}
button2.Text = "后台组件运算";
this.Cursor = Cursors.Default;
Console.WriteLine("aaa");
//MessageBox.Show("RunWorkerCompleted:" + System.AppDomain.GetCurrentThreadId());
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
this.progressBar1.Value = e.ProgressPercentage;
}
}
}