我有List 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()行上的死锁;
我该如何解决?
非常感谢和遗憾的长篇文章!
解决方法:
你是对的.在锁内调用WaitOne可能会导致死锁.并且检查列表是否为空需要在锁内完成,否则可能会有另一个线程尝试删除项目.现在,您的代码看起来很像生产者 – 消费者模式,通常使用阻塞队列实现.如果您使用的是.NET 4.0,那么您可以利用BlockingCollection类.
但是,让我通过几种方式,你可以自己做.第一个使用List和ManualResetEvent来演示如何使用问题中的数据结构来完成此操作.注意在Take方法中使用while循环.
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();
}
}
}