为什么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进行精确记账。 监督合同不包括公平性