代码之家  ›  专栏  ›  技术社区  ›  Xaqron

Monitor.Wait()和Monitor.Pulse()的线程问题

  •  4
  • Xaqron  · 技术社区  · 14 年前

    我在ASP.NET中有一个生产者-消费者场景。我设计了一个 Producer a级 Consumer 类和一个类,用于保存共享对象并负责生产者和消费者之间的通信,让我们称之为 Mediator . 因为我在启动时分叉执行路径(在父对象中),一个线程将调用 Producer.Start() 还有另一个线程调用 Consumer.Start() ,我需要传递一个 调解人 对双方 生产者 消费者 Constructor ). 是一个智能类,它将优化很多东西,比如它的内部队列的长度,但现在将其视为一个循环阻塞队列。 生产者 排队 新对象到 调解人 直到队列满了然后 生产者 会阻塞。 出列 调解人 调解人 班级: Wait() Pulse() . 代码如下:

    Class Mediator
    {
      private object _locker = new object();
    
      public void Wait()
      {
        lock(_locker)
          Monitor.Wait(_locker);
      }
    
      public void Pulse()
      {
        lock(_locker)
          Monitor.Pulse(_locker);
      }
    }
    
    // This way threads are signaling:
    
    Class Consumer
    {
      object x;
      if (Mediator.TryDequeue(out x))
        // Do something
      else
        Mediator.Wait();
    }
    

    我使用的内部调解人 this.Pulse() 每次有东西 排队 出列 所以等待线程将被通知并继续它们的工作。

    设计有问题 或者我在别的地方做错了什么?

    谢谢

    6 回复  |  直到 14 年前
        1
  •  8
  •   Brian Gideon    14 年前

    这里没有太多的代码,但我最好的猜测是 live-lock 问题。如果 Mediator.Pulse 以前打过电话 Mediator.Wait 然后,即使队列中有东西,信号也会丢失。下面是实现阻塞队列的标准模式。

    public class BlockingQueue<T>
    {
      private Queue<T> m_Queue = new Queue<T>();
    
      public void Enqueue(T item)
      {
        lock (m_Queue)
        {
          m_Queue.Enqueue(item);
          Monitor.Pulse(m_Queue);
        }
      }
    
      public T Dequeue()
      {
        lock (m_Queue)
        {
          while (m_Queue.Count == 0) 
          {
            Monitor.Wait(m_Queue);
          }
          return m_Queue.Dequeue();
        }
      }
    }
    

    注意如何 Monitor.Wait 仅当队列为空时调用。还要注意它在 while 循环。这是因为 Wait Enter 一条新的线索 Dequeue 即使打电话给 准备好回来了。如果没有循环,线程可以尝试从空队列中删除项。

        2
  •  4
  •   Ian Mercer    14 年前

    如果你能使用.NET 4,你最好使用 BlockingCollection<T>

        3
  •  2
  •   Xaqron    14 年前

    设计没有错。

    使用时出现问题 Monitor.Wait() Monitor.Pulse() 当你不知道哪个线程要做它的工作首先(生产者或消费者)。在这种情况下,使用 AutoResetEvent 解决问题。当到达应该使用生产者生成的数据的部分时,请考虑消费者。可能在生产商发出脉冲之前到达那里,然后一切正常,但如果消费者在生产商发出信号之后到达那里呢。是的,那么您会遇到一个死锁,因为producer已经调用 脉冲监视器() 对于那一部分,我不会重复。 使用 自动重置事件

    可以用 监视器。等待() 脉冲监视器() 用于发送等待线程的内部中介。

        4
  •  1
  •   davisoa    14 年前

    Producer 电话 Pulse 前/后 Consumer Wait ,然后 等待 documentation 对于 Monitor.Pulse

    而且,你应该知道 object x = new object(); x ,因此创建的对象将不在 TryDequeue 打电话来。

        5
  •  1
  •   doobop    14 年前

    使用提供的代码示例很难判断。

    • 锁在别处吗?在调解人内部?
    • 线程只是停留在获取锁而不是实际的等待调用上吗?
    • 是否已暂停调试器中的线程以查看当前状态?
    • 您是否尝试过一个简单的测试,只需将一个简单的值放在队列上并让它工作?或者调解人在这一点上很复杂?

    在Mediator类和producer类中提供更多细节之前,这是一个疯狂的猜测。似乎有些线程在你不希望锁的情况下控制着它。一旦你发出脉冲,你需要通过退出“lock”作用域来释放线程中的锁。所以,如果你在介质中的某个地方有锁,然后调用Pulse,你需要退出锁所在的最外层范围,而不仅仅是Pulse中的那个。

        6
  •  1
  •   dashton    14 年前

    你能重构成一个普通的消费者/生产者队列吗?这样就可以在一个类中处理加密、解密和线程信号,所以不需要传递公共锁。然后,可以通过委托来处理消隐过程。如果你愿意,我可以举个例子。

    推荐文章