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

时间:2010-10-20 07:26:34

标签: c# multithreading locking

我有列出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();

行上的死锁

我该如何解决?

非常感谢并为这篇长篇文章感到抱歉!

3 个答案:

答案 0 :(得分:2)

你是对的。在锁内调用WaitOne可能会导致死锁。并且检查列表是否为空需要在锁内完成,否则可能会有另一个线程尝试删除项目。现在,您的代码看起来很像生产者 - 消费者模式,通常使用阻塞队列实现。如果您使用的是.NET 4.0,那么您可以利用BlockingCollection类。

但是,让我来看看你能做到的几种方法。第一个使用ListManualResetEvent来演示如何使用问题中的数据结构来完成此操作。请注意在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.WaitMonitor.PulseMonitor.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)

我不明白为什么在零对象的情况下删除对象应该等待添加一个然后删除它。它看起来反对逻辑。