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