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

如何:對(duì)Windows 窗體控件進(jìn)行線程安全調(diào)用

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

示例

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

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

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

Note注意

可以通過(guò)將 CheckForIllegalCrossThreadCalls 屬性的值設(shè)置為 false 來(lái)禁用此異常。這會(huì)使控件以與在 Visual Studio 2003 下相同的方式運(yùn)行。

下面的代碼示例演示如何從輔助線程以線程安全方式和非線程安全方式調(diào)用 Windows 窗體控件。它演示一種以非線程安全方式設(shè)置 TextBox 控件的 Text 屬性的方法,還演示兩種以線程安全方式設(shè)置 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());
}
}
}

對(duì) Windows 窗體控件的非線程安全調(diào)用

對(duì) Windows 窗體控件的非線程安全調(diào)用方式是從輔助線程直接調(diào)用。調(diào)用應(yīng)用程序時(shí),調(diào)試器會(huì)引發(fā)一個(gè) InvalidOperationException,警告對(duì)控件的調(diào)用不是線程安全的。


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

對(duì) Windows 窗體控件的線程安全調(diào)用

對(duì) Windows 窗體控件進(jìn)行線程安全調(diào)用

  1. 查詢控件的 InvokeRequired 屬性。

  2. 如果 InvokeRequired 返回 true,則使用實(shí)際調(diào)用控件的委托來(lái)調(diào)用 Invoke。

  3. 如果 InvokeRequired 返回 false,則直接調(diào)用控件。

在下面的代碼示例中,此邏輯是在一個(gè)稱為 SetText 的實(shí)用工具方法中實(shí)現(xiàn)的。名為 SetTextDelegate 的委托類型封裝 SetText 方法。TextBox 控件的 InvokeRequired 返回 true 時(shí),SetText 方法創(chuàng)建 SetTextDelegate 的一個(gè)實(shí)例,并調(diào)用窗體的 Invoke 方法。這使得 SetText 方法被創(chuàng)建 TextBox 控件的線程調(diào)用,而且在此線程上下文中將直接設(shè)置 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 進(jìn)行的線程安全調(diào)用

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

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

 
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 控件,則在調(diào)試器下運(yùn)行時(shí)可能會(huì)收到線程間 InvalidOperationException。發(fā)生這種情況時(shí),ActiveX 控件不支持多線程處理。有關(guān)使用 Windows 窗體的 ActiveX 控件的更多信息,請(qǐng)參見(jiàn) Windows 窗體和非托管應(yīng)用程序。

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

可靠編程

Caution note警告

使用任何一種多線程時(shí),代碼都容易產(chǎn)生非常嚴(yán)重而復(fù)雜的 bug。有關(guān)更多信息,請(qǐng)?jiān)趯?shí)現(xiàn)使用多線程的任何解決方案之前參見(jiàn)托管線程處理的最佳做法。

請(qǐng)參見(jiàn)

相關(guān)文章

最新評(píng)論