我有列出newJobs 。有些线程将项目添加到该列表中,而其他线程则从中删除项目(如果它不为空)。我有 ManualResetEvent newJobEvent ,它是在项目添加到列表时设置的,并在项目从中删除时重置:
按以下方式将项目添加到列表中:
lock(syncLock){
newJobs.Add(job);
}
newJobEvent.Set();
删除作业的方式如下:
if (newJobs.Count==0)
newJobEvent.WaitOne();
lock(syncLock){
job = newJobs.First();
newJobs.Remove(job);
/*do some processing*/
}
newJobEvent.Reset();
当行
job=newJobs.First()
执行我有时会得到列表为空的异常。我想那张支票:
if (newJobs.Count==0)
newJobEvent.WaitOne();
也应该在lock语句中,但我害怕newJobEvent.WaitOne();
行上的死锁我该如何解决?
非常感谢并为这篇长篇文章感到抱歉!
答案 0 :(得分:2)
你是对的。在锁内调用WaitOne
可能会导致死锁。并且检查列表是否为空需要在锁内完成,否则可能会有另一个线程尝试删除项目。现在,您的代码看起来很像生产者 - 消费者模式,通常使用阻塞队列实现。如果您使用的是.NET 4.0,那么您可以利用BlockingCollection类。
但是,让我来看看你能做到的几种方法。第一个使用List
和ManualResetEvent
来演示如何使用问题中的数据结构来完成此操作。请注意在while
方法中使用Take
循环。
public class BlockingJobsCollection
{
private List<Job> m_List = new List<Job>();
private ManualResetEvent m_Signal = new ManualResetEvent(false);
public void Add(Job item)
{
lock (m_List)
{
m_List.Add(item);
m_Signal.Set();
}
}
public Job Take()
{
while (true)
{
lock (m_List)
{
if (m_List.Count > 0)
{
Job item = m_List.First();
m_List.Remove(item);
if (m_List.Count == 0)
{
m_Signal.Reset();
}
return item;
}
}
m_Signal.WaitOne();
}
}
}
但这不是我怎么做的。我会使用下面的简化解决方案,使用Monitor.Wait
和Monitor.Pulse
。 Monitor.Wait
非常有用,因为可以在锁内部调用。事实上,假设就是这样做的。
public class BlockingJobsCollection
{
private Queue<Job> m_Queue = new Queue<Job>();
public void Add(Job item)
{
lock (m_Queue)
{
m_Queue.Enqueue(item);
Monitor.Pulse(m_Queue);
}
}
public Job Take()
{
lock (m_Queue)
{
while (m_Queue.Count == 0)
{
Monitor.Wait(m_Queue);
}
return m_Queue.Dequeue();
}
}
}
答案 1 :(得分:1)
没有回答您的问题,但如果您使用的是.NET Framework 4,则可以使用新的ConcurrentQueue为您执行所有锁定。
关于你的问题:
我可以想到造成这种问题的一种情况如下:
newJob.Add
,离开锁定。设置并重置锁内的事件,你应该没问题。
答案 2 :(得分:0)
我不明白为什么在零对象的情况下删除对象应该等待添加一个然后删除它。它看起来反对逻辑。