我创建了一个带有文本框的简单Windows窗体,以及“设置”按钮和“切换”按钮。单击切换按钮时,会创建一个线程,重复将文本设置到文本框。当我再次单击该按钮时,线程停止。单击“设置”按钮时,文本框将设置为文本框一次。如果我执行以下操作,则会发生死锁:
你能解释为什么以及在这种情况下如何发生死锁?如何避免呢?
以下是代码:
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;
}
}
}
}
答案 0 :(得分:4)
你能解释一下在这种情况下发生死锁的原因和方式吗?
当然......由于lock()/ Invoke()组合而发生死锁。
当辅助线程正在运行和更新时,它会获得对象的锁定。然后,辅助线程调用Invoke(),这是一个同步调用。关键是要意识到辅助线程在继续之前实际上等待主UI线程更新TextBox。
单击“设置”按钮时,它会尝试更新,但必须等待辅助线程释放锁定。此时,主UI实际上停止并在lock()行冻结,等待辅助线程释放锁定。在等待释放锁时,主UI线程无法处理任何消息。
但是辅助线程在做什么?它目前有锁,正在等待主UI线程为其同步Invoke()调用提供服务。由于主UI线程正在等待释放锁,但它无法处理任何请求(包括Invoke()请求)和bam ... DEADLOCK!他们都在互相等待。
如何避免它?
永远不要在主UI线程中使用lock()。在某些情况下,从Invoke()切换到BeginInvoke()可以解决问题,因为BeginInvoke()是异步的。