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

smw*_*dia -1 c# queue multithreading

根据MSDN

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

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

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

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

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

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

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

Hen*_*man 5

我认为你误解了那个帖子。该问题不是由 Monitor 引起的,而是由该 OP 的 Queue 类中的真正逻辑错误引起的。

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

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

然后您可能需要一种额外的方式(CancellationToken)来结束整个过程。

一个Monitor可以有2种等待线程,实现者选择使用2个队列。使用 1 个队列似乎是可能的,但这不会改变任何事情。它仍然只允许在任何时候运行 1 个线程,所以此时您的理解不知何故出了问题。

正在发生的情况是排队线程可以具有以下两种状态之一:

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

当没有线程等待脉冲时,脉冲/等待机制有点不可靠。一般来说,您不能依靠 Pulse/Wait 来进行精确的簿记。而Monitor的合约不包含公平性