问题描述
我总共有三个线程.第一个是主UI线程,它启动System.Threading.Thread
(ExperimentThread
),然后启动BackgroundWorker
(WorkerThread
).
I have three threads in total. The first is the main UI thread, which starts a System.Threading.Thread
(ExperimentThread
), which in turn starts a BackgroundWorker
(WorkerThread
).
MainThread
和WorkerThread
都访问共享资源.我使用以下对象同步对该资源的访问:
MainThread
and WorkerThread
both access a shared resource. I synchronise access to this resource with the following object:
private static readonly Object LockObject = new Object();
在每个线程的主循环中按如下方式使用:
which I use as follows in the main loop of each thread:
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方法:
And for completeness here is the DoWork method of 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.
}
}
}
当两个线程都自由运行时,这似乎工作正常.
This seems to work fine when both threads are running freely.
在某个时候,UI线程会通过将其布尔字段之一设置为true来终止ExperimentThread
,然后等待其结束,如下所示:
At some point the UI thread will terminate the ExperimentThread
by setting one of its boolean fields to true and then wait for it to end, as follows:
if (ExperimentThread.IsAlive)
{
ExperimentThread.StopThread = true;
ExperimentThread.Join(); // this line seems to cause the deadlock?
}
一旦调用Join(),ExperimentThread
和WorkerThread
正在访问的共享资源就会发生死锁,并且我的应用程序会无限期挂起.这种情况可能发生10次中的9次.
As soon as Join() is called, a deadlock occurs on the shared resource being accessed by ExperimentThread
and WorkerThread
, and my application hangs indefinitely. This happens maybe 9 out of 10 times.
如果我从上面的代码片段中删除了ExperimentThread.Join()
,则永远不会发生死锁,并且ExperimentThread
似乎会优雅地终止(然后继续通过调用CancelAsync()
终止WorkerThread
).
If I remove ExperimentThread.Join()
from the code snippet above, the deadlock never occurs, and ExperimentThread
appears to terminate gracefully (it then goes on to terminate WorkerThread
by calling CancelAsync()
).
任何想法在这里可能是什么问题?
Any ideas what could be the problem here?
(PS我一直在使用Console.WriteLine()来确定何时获取和释放锁,这使我相信存在死锁.是否有更好的方法来确定这一点,我可能是错的吗?)
(P.S. I've been using Console.WriteLine() to determine when locks are taken and released, which is what has lead me to believe there's a deadlock. Is there a better to determine this, I could be wrong?)
推荐答案
一种更好的检查方法是使用并发可视化工具在Visual Studio的更高级别SKU中可用.它将使您确切地看到锁定了每个线程的是什么,以及正在等待线程的是什么,等等.
A better way to check this is to use something like the Concurrency Visualizer available in higher level SKUs of Visual Studio. It will allow you to see exactly what has locked each thread, and what handles threads are waiting on, etc.
由于您陷入僵局的确切原因-没有足够的代码来确定这一点,但是常见的问题是:
As for the exact reason you are getting a deadlock - there isn't enough code to determine this, but common issues are:
-
ExperimentThread
和主线程(带有Join()
调用)都锁定在同一对象上,即在lock(LockObject)
语句中. -
ExperimentThread
使用Control.Invoke
封送回UI线程的调用.由于UI线程被阻止(在Join()
上等待),因此它永远无法处理消息,这将阻止ExperimentThread
完成.
ExperimentThread
and the main thread (with theJoin()
call) are both locking on the same object - ie: within alock(LockObject)
statement.ExperimentThread
is usingControl.Invoke
to marshal a call back onto the UI thread. Since the UI thread is blocked (waiting on theJoin()
), it can never process messages, which will preventExperimentThread
from completing.
话虽如此,如果您使用的是.NET 4或更高版本,通常建议使用Task
或Task<T>
而不是新的Thread
. Task
提供了一个更好的API用于处理线程,包括允许继续而不是阻塞. C#5对此进行了扩展,甚至允许您异步地等待任务完成.
That being said, in general, I would recommend using Task
or Task<T>
instead of a new Thread
if you're using .NET 4 or higher. Task
provides a much nicer API for working with threads, including allowing continuations instead of blocking. C# 5 extends this to even allow you to asynchronously wait for the task to complete.
这篇关于Thread.Join()导致死锁的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!