c# – 检查列表Count属性时出现多线程问题

我有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();
    }
  }
}
上一篇:C#-锁定成本


下一篇:java-JVM如何确保只有一个线程可以获取对象的锁?