欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

如何:對Windows 窗體控件進行線程安全調用

 更新時間:2007年03月09日 00:00:00   作者:  
使用多線程提高 Windows 窗體應用程序的性能時,必須注意以線程安全方式調用控件。

示例

訪問 Windows 窗體控件本質上不是線程安全的。如果有兩個或多個線程操作某一控件的狀態(tài),則可能會迫使該控件進入一種不一致的狀態(tài)。還可能出現(xiàn)其他與線程相關的 bug,包括爭用情況和死鎖。確保以線程安全方式訪問控件非常重要。

.NET Framework 有助于在以非線程安全方式訪問控件時檢測到這一問題。在調試器中運行應用程序時,如果創(chuàng)建某控件的線程之外的其他線程試圖調用該控件,則調試器會引發(fā)一個 InvalidOperationException,并提示消息:“從不是創(chuàng)建控件 control name 的線程訪問它?!?

此異常在調試期間和運行時的某些情況下可靠地發(fā)生。強烈建議您在顯示此錯誤信息時修復此問題。在調試以 .NET Framework 2.0 版之前的 .NET Framework 編寫的應用程序時,可能會出現(xiàn)此異常。

Note注意

可以通過將 CheckForIllegalCrossThreadCalls 屬性的值設置為 false 來禁用此異常。這會使控件以與在 Visual Studio 2003 下相同的方式運行。

下面的代碼示例演示如何從輔助線程以線程安全方式和非線程安全方式調用 Windows 窗體控件。它演示一種以非線程安全方式設置 TextBox 控件的 Text 屬性的方法,還演示兩種以線程安全方式設置 Text 屬性的方法。

 
C#
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 窗體控件的非線程安全調用方式是從輔助線程直接調用。調用應用程序時,調試器會引發(fā)一個 InvalidOperationException,警告對控件的調用不是線程安全的。


 
C#
// 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 的實用工具方法中實現(xiàn)的。名為 SetTextDelegate 的委托類型封裝 SetText 方法。TextBox 控件的 InvokeRequired 返回 true 時,SetText 方法創(chuàng)建 SetTextDelegate 的一個實例,并調用窗體的 Invoke 方法。這使得 SetText 方法被創(chuàng)建 TextBox 控件的線程調用,而且在此線程上下文中將直接設置 Text 屬性。

 
C#
// 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.");
}
 
C#
// 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 進行的線程安全調用

在應用程序中實現(xiàn)多線程的首選方式是使用 BackgroundWorker 組件。BackgroundWorker 組件使用事件驅動模型實現(xiàn)多線程。輔助線程運行 DoWork 事件處理程序,創(chuàng)建控件的線程運行 ProgressChangedRunWorkerCompleted 事件處理程序。注意不要從 DoWork 事件處理程序調用您的任何控件。

下面的代碼示例不異步執(zhí)行任何工作,因此沒有 DoWork 事件處理程序的實現(xiàn)。TextBox 控件的 Text 屬性在 RunWorkerCompleted 事件處理程序中直接設置。

 
C#
// 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。發(fā)生這種情況時,ActiveX 控件不支持多線程處理。有關使用 Windows 窗體的 ActiveX 控件的更多信息,請參見 Windows 窗體和非托管應用程序。

如果您使用的是 Visual Studio,則可以通過禁用 Visual Studio 宿主進程來防止此異常發(fā)生。

可靠編程

Caution note警告

使用任何一種多線程時,代碼都容易產生非常嚴重而復雜的 bug。有關更多信息,請在實現(xiàn)使用多線程的任何解決方案之前參見托管線程處理的最佳做法。

請參見

相關文章

最新評論