2013-10-27 92 views
0

我用一個文本框創建一個簡單的Windows窗體,並設置按鈕和一個切換按鈕。當我點擊切換按鈕時,創建一個線程將文本重複設置到文本框中。當我再次點擊該按鈕時,線程停止。當我點擊設置按鈕時,文本被設置爲文本框一次。如果我執行以下操作,會發生死鎖:爲什麼在這個特定的WinForms應用程序中發生死鎖?

  1. 運行應用程序(在調試模式下)。
  2. 點擊切換按鈕讓文本在文本框中運行。
  3. 單擊設置按鈕。 - >在這一步發生死鎖。

你能解釋爲什麼,以及如何僵局出現這種狀況?如何避免它?

下面是代碼:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Windows.Forms; 

namespace DeadLockTest 
{ 
    static class Program 
    { 
     /// <summary> 
     /// The main entry point for the application. 
     /// </summary> 
     [STAThread] 
     static void Main() 
     { 
      Application.EnableVisualStyles(); 
      Application.SetCompatibleTextRenderingDefault(false); 
      Application.Run(new Form1()); 
     } 
    } 
} 

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Threading; 
using System.Windows.Forms; 

namespace DeadLockTest 
{ 
    public class Form1 : Form 
    { 
     private System.Windows.Forms.TextBox textBox1; 
     private System.Windows.Forms.Button button1; 
     private System.Windows.Forms.Button button2; 

     private int counter; 
     private Thread thread; 
     private bool cancelRequested; 
     private string content; 
     private object lockKey = new object(); 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     protected void UpdateContent() 
     { 
      this.textBox1.Text = this.content; 
     } 

     protected void InvokeUpdateContent() 
     { 
      lock (this.lockKey) 
      { 
       if (InvokeRequired) 
       { 
        Invoke(new Action(UpdateContent)); 
       } 
       else 
       { 
        UpdateContent(); 
       } 
      } 
     } 

     protected void SetText(string text) 
     { 
      this.content = text; 
      InvokeUpdateContent(); 
     } 

     protected void StressTest() 
     { 
      int localCounter = 0; 

      while (!this.cancelRequested) 
      { 
       SetText(string.Format("{0}", localCounter++)); 
      } 

      this.cancelRequested = false; 
      this.thread = null; 
     } 

     private void InitializeComponent() 
     { 
      this.textBox1 = new System.Windows.Forms.TextBox(); 
      this.button1 = new System.Windows.Forms.Button(); 
      this.button2 = new System.Windows.Forms.Button(); 
      this.SuspendLayout(); 
      // 
      // textBox1 
      // 
      this.textBox1.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 
         | System.Windows.Forms.AnchorStyles.Right))); 
      this.textBox1.Location = new System.Drawing.Point(12, 12); 
      this.textBox1.Name = "textBox1"; 
      this.textBox1.ReadOnly = true; 
      this.textBox1.Size = new System.Drawing.Size(260, 20); 
      this.textBox1.TabIndex = 0; 
      // 
      // button1 
      // 
      this.button1.Location = new System.Drawing.Point(12, 38); 
      this.button1.Name = "button1"; 
      this.button1.Size = new System.Drawing.Size(75, 23); 
      this.button1.TabIndex = 1; 
      this.button1.Text = "Set"; 
      this.button1.UseVisualStyleBackColor = true; 
      this.button1.Click += new System.EventHandler(this.button1_Click); 
      // 
      // button2 
      // 
      this.button2.Location = new System.Drawing.Point(93, 38); 
      this.button2.Name = "button2"; 
      this.button2.Size = new System.Drawing.Size(75, 23); 
      this.button2.TabIndex = 2; 
      this.button2.Text = "Toggle"; 
      this.button2.UseVisualStyleBackColor = true; 
      this.button2.Click += new System.EventHandler(this.button2_Click); 
      // 
      // Form1 
      // 
      this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 
      this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 
      this.ClientSize = new System.Drawing.Size(284, 262); 
      this.Controls.Add(this.button2); 
      this.Controls.Add(this.button1); 
      this.Controls.Add(this.textBox1); 
      this.Name = "Form1"; 
      this.Text = "Form1"; 
      this.ResumeLayout(false); 
      this.PerformLayout(); 

     } 

     private void button1_Click(object sender, EventArgs e) 
     { 
      SetText(string.Format("{0}", this.counter++)); 
     } 

     private void button2_Click(object sender, EventArgs e) 
     { 
      if (this.thread == null) 
      { 
       this.thread = new Thread(new ThreadStart(StressTest)); 

       thread.Start(); 
      } 
      else 
      { 
       this.cancelRequested = true; 
      } 
     } 
    } 
} 

回答

4

你能解釋爲什麼,以及如何僵局出現這種狀況?

當然...死鎖發生是因爲lock()/ Invoke()組合。

雖然次級線程運行和更新,它獲得對象上的鎖。然後輔助線程調用Invoke(),這是一個同步調用。認識到輔助線程在繼續之前實際上等待主UI線程更新文本框是關鍵。

當你點擊設置按鈕,它會嘗試更新,但必須等待鎖由輔助線程被釋放。此時,主UI實際上停止並凍結在lock()行,等待輔助線程釋放鎖。在等待釋放鎖的同時,主UI線程無法處理任何消息。

但是輔助線程在做什麼?它目前有鎖,正在等待主UI線程爲其同步Invoke()調用提供服務。由於主UI線程正在等待釋放鎖,但它不能處理任何請求(包括Invoke()請求)和bam ... DEADLOCK!他們都在等待對方。

如何避免呢?

從不使用鎖()從主UI線程。在某些情況下,從Invoke()切換到BeginInvoke()可以解決問題,因爲BeginInvoke()是異步的。

相關問題