以下C#类用于多线程环境。我删除了很多实际的代码。几乎同时调用MethodA和MethodB时会出现问题。 IsDepleted属性中的锁定顺序不能解决问题。从IsDepleted属性中删除锁(WaitingQueue)可以解决死锁,但是当另一个线程在WaitingQueue.Count == 0和Processing.Count == 0语句之间添加/删除WaitingQueue中的项时,此解决方案会出现问题。
using System.Collections.Generic;
class Example
{
bool IsDepleted
{
get
{
lock (Processing)
{
lock (WaitingQueue)
{
return WaitingQueue.Count == 0
&& Processing.Count == 0;
}
}
}
}
private readonly List<object> Processing = new List<object>();
private readonly Queue<object> WaitingQueue = new Queue<object>();
public void MethodA(object item)
{
lock (WaitingQueue)
{
if (WaitingQueue.Count > 0)
{
if (StartItem(WaitingQueue.Peek()))
{
WaitingQueue.Dequeue();
}
}
}
}
public void MethodB(object identifier)
{
lock (Processing)
{
Processing.Remove(identifier);
if (!IsDepleted)
{
return;
}
}
//Do something...
}
bool StartItem(object item)
{
//Do something and return a value
}
}
答案 0 :(得分:3)
获取方法A中的Processing锁定和方法B中的WaitingQueue锁定(换句话说,使其看起来像第一个代码块)。这样,你总是以同样的顺序拿锁,你永远不会死锁。
答案 1 :(得分:3)
这取决于您是否需要快速修复或严格修复。
快速修复只是在所有情况下使用一个锁定对象。
e.g。 private readonly object _lock = new object();
然后锁定它。但是,根据您的情况,这可能会影响您的表现,而不是您可以接受的。
即。你的代码会变成这样:
using System.Collections.Generic;
class Example
{
private readonly object _lock = new object();
bool IsDepleted
{
get
{
lock (_lock)
{
return WaitingQueue.Count == 0
&& Processing.Count == 0;
}
}
}
private readonly List<object> Processing = new List<object>();
private readonly Queue<object> WaitingQueue = new Queue<object>();
public void MethodA(object item)
{
lock (_lock)
{
if (WaitingQueue.Count > 0)
{
if (StartItem(WaitingQueue.Peek()))
{
WaitingQueue.Dequeue();
}
}
}
}
public void MethodB(object identifier)
{
lock (_lock)
{
Processing.Remove(identifier);
if (!IsDepleted)
{
return;
}
}
//Do something...
}
bool StartItem(object item)
{
//Do something and return a value
}
}
答案 2 :(得分:2)
简化代码并仅使用单个对象锁定。您也可以用以下代码替换锁:
Monitor.TryEnter(处理1000)
这会给你1秒的超时。基本上是这样的:
if (Monitor.TryEnter(Processing, 1000))
{
try
{
//do x
}
finally
{
Monitor.Exit(Processing);
}
}
现在你不会停止死锁,但你可以处理你没有锁定的情况。