【问题标题】:Waiting queue vs. Ready queue等待队列与就绪队列
【发布时间】:2013-06-06 22:18:04
【问题描述】:

为什么在使用监视器时每个对象都有一个就绪和等待队列?如果一个线程完成了一个对象,只需弹出等待队列中的下一个项目。就绪队列似乎是多余的。

上下文是 .NET,更具体地说是监视器类。

【问题讨论】:

  • 您需要提供上下文。当您提出这个问题时,您是否正在阅读一些东西?如果是这样,请链接到它或在此处重复上下文。
  • "in .NET" - 什么类?监视器?还有什么?
  • 听起来您在谈论 Windows 线程,而不是专门讨论 .NET。请澄清。
  • 了解监视器的工作方式很重要。专注于 Wait 和 Pulse 方法,而不是 Enter 和 Exit。

标签: .net


【解决方案1】:

生产者/消费者队列是学习 Monitor 类的一个非常好的用例。

假设工作被偶尔添加到队列中。有时需要处理的项目很多,有时会经过很长时间而队列中没有项目。

所以,假设您有 k 个消费者线程等待处理队列中的项目。换句话说,每个线程都实现了一个紧密的循环,不断地尝试

  1. 请求在队列中获取lock(这会将您置于监视器的“就绪”队列中)。
  2. 获得锁后,检查队列是否有任何项目。如果没有,您有两种释放锁的选择: a) 调用 Monitor.Exit,然后调用 Monitor.Enter 将自己置于“就绪”队列的后面。 b) 或者,调用 Monitor.Wait,将自己置于 Monitor 的“等待”队列中。

如果您选择选项“a”,您的 k 个线程将浪费 CPU 周期,一遍又一遍地在就绪队列中移动,找不到工作要做,并在队列后面重新开始。

如果您选择选项“b”,您的意思是“现在这里无事可做,让我睡觉,有事可做时叫醒我”。

使用选项“b”,如果队列为空,您很快就会发现所有消费者都在“等待”队列中休眠,不会浪费 CPU 时间。

然后,当生产者将项目添加到队列时,它会调用 Monitor.Pulse。这会唤醒“等待”队列中的第一个线程,然后该线程会进入“就绪”队列的后面(如果“就绪”队列为空,该队列也是队列的前面)。

当一个线程获得锁并从队列中消费这个项目时,它会再次调用 Monitor.Enter,并进入“就绪”队列的后面。

请参阅this article,了解 .NET 中生产者/消费者队列的经典实现。

【讨论】:

  • 感谢您的详细回答,但您的想法可以通过等待队列(睡眠线程队列)轻松实现。为什么对 Monitor.Pulse 的调用不能将等待队列的第一个元素出列,即为什么它首先必须进入就绪队列?
  • @user1316459 - 假设所有的消费者都很忙,因为更多的项目被添加到任务队列中(所有的 Monitor.Pulse 调用都没有使用,因为消费者已经醒了并且很忙)。如果只有一个等待队列,那么当每个消费者完成当前任务时,它会在等待队列中重新休眠,不会有任何消费者醒来处理新进来的工作。
  • @user1316459 - “但是你的想法可以很容易地通过等待队列来实现”。我邀请你这样做,并在博客中公开。也许你会想出一些新的东西。或者您可能不会,但在此过程中您会深入了解所有问题。
  • "如果只有一个等待队列,那么当每个消费者完成其当前任务时,它会在等待队列中重新进入睡眠状态" 不会。在每个消费者完成它的工作后,它会尝试获取锁。这里只会发生两件事:1. 它需要锁,接受一个任务并释放锁。没问题。 2. 它不能要求锁,因为另一个消费者正在接受一个任务。因此,它进入睡眠(等待)队列。另一个消费者现在已经完成了它的任务并脉冲睡眠(等待)队列。因此,原来的消费者现在可以重试了。
  • @user1316459 - 我不明白你的方法是如何工作的,答案的 cmets 部分并不是真正进行长时间讨论的地方。但是请继续尝试,如果您有什么想法,请告诉所有人。
【解决方案2】:

假设我已经正确理解了您的问题,那么两者的用途不同。

“就绪”队列用于准备好在获得锁后立即运行的线程。他们只是在等待获取锁。这主要用于互斥,防止两个线程同时使用同一个资源。

“等待”队列用于等待特定信号的线程 - 即正在脉冲的监视器。这通常用于协调 - 例如在生产者/消费者队列中,如果队列为空,消费者将等到监视器发出脉冲,然后再次检查队列。虽然没有人在生产,也没有人在消费,但没有线程拥有监视器 - 没有资源在使用。但是监视器是用来协调消费者和生产者的。

【讨论】:

  • ...或者使用巫毒读心术来弄清楚问题在问什么。
  • @TimothyShields:嗯,我搜索了“就绪队列”和“等待队列”,发现了这个问题:*.com/questions/12121151/… - 我怀疑这个问题使用的是相同的术语。
  • 所以你是说只要有东西准备好,脉冲就会通知消费者,同时消费者线程可以做其他事情。我不认为这是可能的,因为脉冲只通知等待队列中的线程,而这些线程已经被阻塞,所以它们不能做任何事情。
  • @user1316459 - “所以与此同时,消费者线程可以做其他事情”。不,恰恰相反。等待队列中的线程阻塞,因此它们不会消耗 CPU 周期,一遍又一遍地锁定队列以查看是否有任何东西要消耗。