是的,生产者(或插入者)将在消费者持有锁的同时被阻止。请注意,通过调用Monitor.Wait
释放该锁,然后在控制流返回给调用者时重新获取该锁。所有这些都假设你的制作者试图获得相同的锁定。
作为一个方面说明,你有消费者编码的方式效率稍低一些。因为你有一个continue
陈述,我不得不假设while
循环包装了lock
,这可能使你的代码看起来更像以下内容。
object msg = null;
while (msg == null)
{
lock (MsgQueue)
{
if (MsgQueue.Count == 0)
{
Monitor.Wait(MsgQueue);
continue;
}
msg = MsgQueue.Dequeue();
}
}
这可能进行重构,以便等待条件的lock
块内复查。这样您就不必释放并重新获取锁来执行检查。
object msg = null;
lock (MsgQueue)
{
while (MsgQueue.Count == 0)
{
Monitor.Wait(MsgQueue);
}
msg = MsgQueue.Dequeue();
}
再次,因为我看到了continue
声明我假设大家都知道,等待条件必须总是Wait
后复查。但是,如果你不知道这个要求,我会在这里说明它,因为它很重要。
如果等待条件没有被重新检查,并且有2个或更多的消费者,那么他们中的一个可以进入锁内,并将最后一个项目出列。即使其他消费者通过致电Pulse
或PulseAll
从等候队列移动到就绪队列,但这仍然可能发生,但它没有机会在第一个消费者面前重新获取锁定。显然,如果没有重新检查,消费者可能会尝试在空队列上运行。在生产端是否使用Pulse
或PulseAll
并不重要。仍然存在问题,因为Monitor
不优先于Enter
之上的Wait
。
更新:
我忘了指出,如果你使用的是.NET 4.0,那么你可以采取的BlockingCollection这是一个阻塞队列的实现优势。对于多个生产者和消费者来说这是安全的,并且如果队列为空,则为你完成所有阻塞。
要验证您在评论中所说的内容,可以发表一篇关于Enqueue如何的短文吗? – 2011-06-17 12:49:23
如果你使用的是.Net 4.0,你可以在这里使用`ConcurrentQueue`,参见http://msdn.microsoft.com/en-us/library/dd267265.aspx – 2011-06-17 15:20:19
我建议你使用[BlockingCollection](http:如果C#4.0或队列与[AutoRestEvent](http://msdn.microsoft.com/en-us/library/system.threading.autoresetevent。 aspx)检查[这在stacoverflow](http://stackoverflow.com/questions/6397566/multithreaded-net-queue-problems-c/6404581#6404581) – 2011-06-20 04:07:24