我总共有三个帖子。第一个是主UI线程,它启动System.Threading.Thread
(ExperimentThread
),然后开始BackgroundWorker
(WorkerThread
)。
MainThread
和WorkerThread
都访问共享资源。我使用以下对象同步对此资源的访问:
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
:
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?
}
一旦调用了Join(),ExperimentThread
和WorkerThread
访问的共享资源就会发生死锁,我的应用程序将无限期挂起。这可能发生在10次中的9次。
如果我从上面的代码段中删除ExperimentThread.Join()
,则永远不会发生死锁,ExperimentThread
似乎正常终止(然后通过调用{{1}继续WorkerThread
})。
任何想法可能会出现问题吗?
(PS我一直在使用Console.WriteLine()来确定何时获取和释放锁定,这使我相信存在死锁。有没有更好的判断,我可能是错的?)
答案 0 :(得分:1)
有没有更好的确定,我可能是错的?
检查此问题的更好方法是使用Visual Studio的更高级别SKU中提供的Concurrency Visualizer之类的内容。它将允许您确切地查看锁定每个线程的内容,以及线程正在等待的处理等等。
至于你遇到僵局的确切原因 - 没有足够的代码来确定这一点,但常见问题是:
ExperimentThread
并且主线程(带有Join()
调用)都锁定在同一个对象上 - 即:在lock(LockObject)
语句中。ExperimentThread
正在使用Control.Invoke
将回调编组回UI线程。由于UI线程被阻止(等待Join()
),它永远无法处理消息,这将阻止ExperimentThread
完成。话虽如此,一般情况下,如果您使用的是.NET 4或更高版本,我建议您使用Task
或Task<T>
而不是新的Thread
。 Task
提供了一个更好的API来处理线程,包括允许继续而不是阻塞。 C#5将其扩展为甚至允许您异步等待任务完成。