死鎖(兩個執行緒在彼此等待)

當兩個或多個執行緒等待彼此完成或以永遠等待的方式釋放資源時,會發生死鎖。

兩個執行緒等待彼此完成的典型情況是,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
}

為了最大限度地降低遇到相互等待死鎖的風險,請儘可能避免執行緒之間的迴圈引用。執行緒層次結構,低階別執行緒只為較高階別的執行緒留下訊息而從不等待它們不會遇到這種問題。但是,它仍然容易受到基於資源鎖定的死鎖的影響。