2013-07-10 189 views
1

我總共有三個線程。第一個是主UI線程,該線程啓動System.Threading.ThreadExperimentThread),後者又啓動BackgroundWorkerWorkerThread)。Thread.Join()導致死鎖

MainThreadWorkerThread都訪問共享資源。我同步與下列對象訪問此資源:

private static readonly Object LockObject = new Object(); 

,我用在每個線程的主循環如下:

lock (LockObject) 
{ 
    // Do something with shared resource here. 
} 

ExperimentThread一個簡化版本如下:

public void RunExperiment 
{ 
    while (!bStopThread) 
    { 

     lock (LockObject) 
     { 
      // Do something with shared resource here. 
     } 

     if (bStopThread) 
     { 
      break; 
     } 
     else 
     { 
      Application.DoEvents(); 
      Thread.Sleep(250); 
     } 
    } 
} 

而對於完整性這裏是WorkerThread的DoWork的方法:

private void Worker_DoWork(object sender, DoWorkEventArgs e) 
{ 
    BackgroundWorker Worker = sender as BackgroundWorker; 

    for (int X = 0; X < 200; X++) 
    { 
     if (Worker.CancellationPending) 
     { 
      e.Cancel = true; 
      return; 
     } 

     lock (LockObject) 
     { 
      // Do something with shared resource here. 
     } 
    } 
} 

這似乎工作正常,當兩個線程都自由運行。

在一些點,UI線程將通過設置它的布爾字段設置爲true的一個終止ExperimentThread,然後等待它結束,如下所示:一旦

if (ExperimentThread.IsAlive) 
{ 
    ExperimentThread.StopThread = true; 
    ExperimentThread.Join(); // this line seems to cause the deadlock? 
} 

爲加入()被調用時,正在訪問的共享資源上出現死鎖ExperimentThreadWorkerThread,並且我的應用程序無限期地掛起。這可能發生在10次中的9次。

如果從我上面的代碼段刪除ExperimentThread.Join(),僵局永遠不會發生,並ExperimentThread似乎正常終止(它然後繼續通過調用CancelAsync()終止WorkerThread)。

任何想法可能是這裏的問題? (PS我一直在使用Console.WriteLine()來確定什麼時候獲取和釋放鎖,這是什麼導致我相信有一個死鎖。有沒有更好的來確定這一點,我可能是錯的? )

+1

你可以包含你的'ExperimentThread'執行的代碼嗎? –

+0

沒有足夠的代碼顯示此處以確定真正原因... –

+0

'Thread.Join()導致死鎖' - yup,這只是Join()的正常行爲,或任何其他類型的硬等待,在GUI應用程序事件處理程序。我已經看到幾十年來以各種語言使用各種語言死鎖的圖形用戶界面,但我們仍然得到'你必須等待線程從加入/等待/任何'結束從愚蠢的線程介紹網站。 35年來爲缺乏經驗的多線程開發人員餵養了同樣的垃圾。 –

回答

1

有沒有更好的判斷這個,我可能是錯的?

檢查此問題的更好方法是使用類似於Visual Studio更高級SKU中提供的Concurrency Visualizer。它可以讓你看到究竟是什麼鎖定每個線程,以及線程正在等待什麼等。

至於確切的原因是你得到一個死鎖 - 沒有足夠的代碼來確定這一點,但普通問題是:

  1. ExperimentThread和主線程(與Join()電話)都鎖定在同一對象上 - 即:一個lock(LockObject)語句中。使用Control.Invoke編組回調到UI線程。由於UI線程被阻塞(等待Join()),它永遠不會處理消息,這將阻止ExperimentThread完成。

話雖這麼說,在一般情況下,我會建議使用TaskTask<T>,而不是一個新的Thread如果你使用.NET 4.0或更高版本。 Task爲使用線程提供了更好的API,包括允許繼續而不是阻塞。 C#5對此進行了擴展,甚至允許您異步地等待任務完成。

+0

我的BackgroundWorker確實使用Control.Invoke來更新UI線程上的某些東西。我想你已經回答了我的問題。非常感謝,並很好地推斷出缺少代碼:-) –

+0

@digital_fate是的 - 在線程中調用'Invoke()',然後從UI線程調用'Join()'會導致死鎖。 –

+0

除了取消對「Join()」的調用,我不確定如何繼續。無論哪種方式,您的答案都是正確的,我應該在問題的最後添加一些內容來解釋問題所在? –