首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Monitor.Wait()和Monitor.Pulse()的线程问题

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

Stack Overflow用户
提问于 2010-10-18 01:43:38
回答 6查看 4.4K关注 0票数 4

我在ASP.NET中有一个生产者-消费者场景,我设计了一个Producer类、一个Consumer类和一个用于保存共享对象的类,并负责生产者和消费者之间的通信,让我们称之为Mediator。因为我在启动时(在父对象中)发送执行路径,一个线程将调用Producer.Start(),另一个线程将调用Consumer.Start(),因此需要将Mediator的引用传递给ProducerConsumer (通过Constructor)。Mediator是一个智能类,它将优化许多事情,比如它的内部队列的长度,但目前将它视为一个循环阻塞队列。Producer会将新对象排队到Mediator,直到队列满了,然后Producer会阻塞。Consumer将对象从Mediator中排出,直到队列中没有任何内容。对于线程之间的信令,我在Mediator类中实现了两个方法:Wait()Pulse()。代码是这样的:

代码语言:javascript
复制
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(),这样等待线程就会收到信号并继续工作。

但是我遇到了死锁,因为我从来没有使用过这种设计来发送消息,所以我不确定是设计出了什么问题,还是在其他地方做错了什么?

谢谢

EN

回答 6

Stack Overflow用户

回答已采纳

发布于 2010-10-19 03:06:00

设计没有什么不对。

当您使用Monitor.Wait()Monitor.Pulse()时,当您不知道哪个线程将首先执行它的工作时(生产者还是消费者),就会产生问题。在这种情况下,使用AutoResetEvent解决问题。当它到达应该使用生产者生成的数据的部分时,请考虑消费者。也许它在生产者发出信号之前就到达了那里,那么一切都很好,但是如果消费者在生产者发出信号之后到达那里呢?是的,然后您会遇到一个死锁,因为生产者已经为该部分调用了Monitor.Pulse(),并且不会重复它。使用AutoResetEvent,您可以确定使用者在那里等待生产者发出的信号,如果生产者在使用者到达该部分之前已经发出信号,则门是打开的,并且使用者将继续。

在Mediator中使用Monitor.Wait()Monitor.Pulse()来发送等待线程的消息是可以的。

票数 2
EN

Stack Overflow用户

发布于 2010-10-18 13:04:17

这里没有太多的代码可以继续,但我最好的猜测是,您有一个活锁问题。如果Mediator.PulseMediator.Wait之前被调用,那么即使队列中有什么东西,信号也会丢失。下面是实现阻塞队列的标准模式。

代码语言:javascript
复制
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的新线程可能会占用最后一项,即使对Wait的调用已经准备好返回。如果没有循环,线程可以尝试从空队列中删除项。

票数 8
EN

Stack Overflow用户

发布于 2010-10-18 01:48:33

如果您可以使用.NET 4,最好的选择是使用BlockingCollection<T> (http://msdn.microsoft.com/en-us/library/dd267312.aspx)来处理排队、退队列和队列长度的限制。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/3956127

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档