溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

C#如何解決跨線程調(diào)用窗體控件引發(fā)的線程安全問(wèn)題

發(fā)布時(shí)間:2021-08-12 12:34:01 來(lái)源:億速云 閱讀:156 作者:小新 欄目:編程語(yǔ)言

這篇文章將為大家詳細(xì)講解有關(guān)C#如何解決跨線程調(diào)用窗體控件引發(fā)的線程安全問(wèn)題,小編覺(jué)得挺實(shí)用的,因此分享給大家做個(gè)參考,希望大家閱讀完這篇文章后可以有所收獲。

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

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

在未使用 Invoke 方法的情況下,從不是創(chuàng)建某個(gè)控件的線程的其他線程調(diào)用該控件是不安全的。 以下非線程安全的調(diào)用的示例。

// 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 可幫助您檢測(cè)以非線程安全方式訪問(wèn)控件這一問(wèn)題。 在調(diào)試器中運(yùn)行應(yīng)用程序時(shí),如果一個(gè)不是創(chuàng)建某個(gè)控件的線程的其他線程調(diào)用該控件,則調(diào)試器會(huì)引發(fā)一個(gè) InvalidOperationException,并顯示以下消息:“從不是創(chuàng)建控件控件名稱 的線程訪問(wèn)它。”

此異常在調(diào)試期間和運(yùn)行時(shí)的某些情況下可靠地發(fā)生。 在調(diào)試以 .NET Framework 2.0 版之前的 .NET Framework 編寫的應(yīng)用程序時(shí),可能會(huì)出現(xiàn)此異常。 我們強(qiáng)烈建議您在發(fā)現(xiàn)此問(wèn)題時(shí)進(jìn)行修復(fù),但您可以通過(guò)將 CheckForIllegalCrossThreadCalls 屬性設(shè)置為 false 來(lái)禁用它。(不推薦)

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

查詢控件的 InvokeRequired 屬性。

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

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

在下面的代碼示例中,將在由后臺(tái)線程執(zhí)行的 ThreadProcSafe 方法中實(shí)現(xiàn)線程安全調(diào)用。 如果 TextBox 控件的 InvokeRequired 返回 true,則 ThreadProcSafe 方法會(huì)創(chuàng)建 SetTextCallback 的一個(gè)實(shí)例,并將該實(shí)例傳遞給窗體的 Invoke 方法。 這使得 SetText 方法被創(chuàng)建 TextBox 控件的線程調(diào)用,而且在此線程上下文中將直接設(shè)置 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());
  }

 }
}

關(guān)于“C#如何解決跨線程調(diào)用窗體控件引發(fā)的線程安全問(wèn)題”這篇文章就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,使各位可以學(xué)到更多知識(shí),如果覺(jué)得文章不錯(cuò),請(qǐng)把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI