死锁(两个线程在彼此等待)

当两个或多个线程等待彼此完成或以永远等待的方式释放资源时,会发生死锁。

两个线程等待彼此完成的典型情况是,Windows 窗体 GUI 线程等待工作线程,并且工作线程尝试调用 GUI 线程管理的对象。请注意,使用此代码,单击 button1 将导致程序挂起。

private void button1_Click(object sender, EventArgs e)
{
    Thread workerthread= new Thread(dowork);
    workerthread.Start();
    workerthread.Join();
    // Do something after
}

private void dowork()
{
    // Do something before
    textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));
    // Do something after
}

workerthread.Join() 是一个阻塞调用线程的调用,直到 workerthread 完成。textBox1.Invoke(invoke_delegate) 是一个阻塞调用线程的调用,直到 GUI 线程处理了 invoke_delegate,但是如果 GUI 线程已经在等待调用线程完成,则此调用会导致死锁。

要解决这个问题,可以使用非阻塞方式调用文本框:

private void dowork()
{
    // Do work
    textBox1.BeginInvoke(new Action(() => textBox1.Text = "Some Text"));
    // Do work that is not dependent on textBox1 being updated first
}

但是,如果你需要运行依赖于首先更新的文本框的代码,这将导致问题。在这种情况下,将其作为调用的一部分运行,但请注意,这将使其在 GUI 线程上运行。

private void dowork()
{
    // Do work
    textBox1.BeginInvoke(new Action(() => {
        textBox1.Text = "Some Text";
        // Do work dependent on textBox1 being updated first, 
        // start another worker thread or raise an event
    }));
    // Do work that is not dependent on textBox1 being updated first
}

或者从整个新线程开始,让那个人在 GUI 线程上等待,以便 workerthread 完成。

private void dowork()
{
    // Do work
    Thread workerthread2 = new Thread(() =>
    {
        textBox1.Invoke(new Action(() => textBox1.Text = "Some Text"));
        // Do work dependent on textBox1 being updated first, 
        // start another worker thread or raise an event
    });
    workerthread2.Start();
    // Do work that is not dependent on textBox1 being updated first
}

为了最大限度地降低遇到相互等待死锁的风险,请尽可能避免线程之间的循环引用。线程层次结构,低级别线程只为较高级别的线程留下消息而从不等待它们不会遇到这种问题。但是,它仍然容易受到基于资源锁定的死锁的影响。