2013-07-10 187 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()”的调用,我不确定如何继续。无论哪种方式,您的答案都是正确的,我应该在问题的最后添加一些内容来解释问题所在? –