我在 ASP.NET 中有一个生产者-消费者场景。我设计了一个Producer
类,一个Consumer
类和一个用于保存共享对象并负责Producer 和Consumer 之间通信的类,我们称它为Mediator
。因为我在启动时 fork 执行路径(在父对象中)并且一个线程会调用 Producer.Start()
而另一个线程会调用 Consumer.Start()
,我需要将 Mediator
的引用传递给 Producer
和 Consumer
(通过 Constructor
)。 Mediator
是一个智能类,它将优化许多事情,例如它的内部队列的长度,但现在将其视为循环阻塞队列。 Producer
会将新对象排队到Mediator
,直到队列满为止,然后Producer
会阻塞。 Consumer
dequeues 来自 Mediator
的对象,直到队列中没有任何对象。对于线程之间的信号,我在 Mediator
类中实现了两个方法: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();
}
在 Mediator 内部,每次入队或出队时,我都会使用this.Pulse()
,这样等待的线程就会收到信号并继续它们的工作.
但我遇到了死锁,因为我从未使用过这种设计来发送信号线程,所以我不确定是设计有问题还是我在其他地方做错了什么?
谢谢
最佳答案
这里没有太多代码可以继续,但我最好的猜测是你有一个 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
的新线程可能会占用最后一项,即使调用Wait
准备返回。如果没有循环,线程可能会尝试从空队列中删除一个项目。
关于c# - Monitor.Wait() 和 Monitor.Pulse() 的线程问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3956127/