为什么Monitor类保留2个队列:“就绪”和“等待”?

根据MSDN :

Monitor类由静态(在C#中)或Shared(在Visual Basic中)方法组成,这些方法对控制对关键部分的访问的对象进行操作。 为每个同步对象维护以下信息:

  • 对当前持有锁的线程的引用。

  • 就绪队列的引用,其中包含准备获取锁的线程。

  • 等待队列的引用,其中包含等待通知锁定对象状态更改的线程。

在这个线程中 ,2个队列引起了一些微妙的问题。

我认为上述线程中问题的根本原因是有2个队列。 如果只有一个队列,则每当Monitor.Pulse() ,只能调度该单个队列中的一个线程来运行。 多个线程无法同时处于就绪状态。 所以这个问题永远不会发生。

那么为什么Monitor保留2个队列呢?

我觉得你误解了SOpost。 问题不是由Monitor引起的,而是由OP的Queue类中的真正逻辑错误引起的。

删除是一种熟悉的模式,它应该使用一段while不是if

 lock (q) { // if (q.Count == 0) while (q.Count == 0) { Monitor.Wait(q); } ... // use it, we are now sure that q.Count > 0 } 

然后你可能想要一个额外的方法(CancellationToken)来结束整个过程。

Monitor可以有2种等待线程,实现者选择使用2个队列。 使用1队列似乎是可能的,但这不会改变一件事。 它仍然只允许1个线程在任何时间运行,所以你的理解在某种程度上是错误的。

发生的事情是排队的线程可以有两种状态之一:

  • 当一个线程调用lock(q)并且已经锁定时,它将排队为Ready
  • 当一个线程调用Wait(q)它会排队等待,并需要一个Pulse()来唤醒
  • 调用Pulse()时,1等待线程将移至Ready状态。 但它不会立即运行,它将不得不轮流等待。 根据定义,只能在执行锁定时调用Pulse()。
  • 当脉冲线程被重新激活时,情况可能已经改变(即,另一个线程消耗了数据元素)。

脉冲/等待机制有点不可靠,当没有线程等待脉冲被忽视时。 您通常不能依靠Pulse / Wait进行精确记账。 监督合同不包括公平性