我正在学习同步问题,并阅读了生产者消费者问题和睡眠理发师问题。
我发现生产者消费者问题与睡眠理发师问题非常相似。坦率地说,我找不到它们之间的区别。
让我说... 当生产者制造产品时,他将其添加到队列中;当顾客到达理发店时,他会去候诊室。 当然,顾客去理发店,如果他正在睡觉,就会叫醒他。在产品消费者问题中似乎类似。如果队列为空,消费者可能会睡觉,当新产品添加到队列中时,应该有人唤醒他。
当队列满员时,生产者不应该生产更多的产品,这样他就可以睡得更好。当消费者从队列中消费产品并且队列中有位置时,生产者应该醒来(或应该被某人唤醒)工作。 我认为这在睡眠理发师问题中是相似的。当等候室满员时,新来的顾客可以在等候室等候。当候诊室里有人去理发时,外面的顾客可以进入候诊室。(当然,如果候诊室里没有空椅子,客户可以回家,但我认为这并没有太大的区别)
我认为解决这两个问题的实现是相似的。在各种版本的实现中,我看到使用两个信号量和一个互斥锁的实现。两个信号量用于唤醒处于睡眠状态的参与者,一个互斥锁用于防止并发访问导致数据区域损坏。 我相信这个解决方案可以解决这两个问题。所以我觉得生产者消费者问题和睡眠理发师问题之间没有区别。
生产者-消费者问题是关于生产者和消费者有不同的throughputs
,所以在生产者生产任务比消费者执行它们更快的情况下,那么它们之间有任务的队列大小将增长,因此从任务到达队列的那一刻起,消费者从队列中取出它的时间就会增加, 最终你会失去记忆。
睡眠理发师问题大约是race conditions
.想象一下,你有相同的生产者生成任务(来理发店的人)和消费者(理发师)。如果不这样做busy waiting
当队列中没有更多任务时,您的使用者会睡觉,因此当新任务到达时,它会首先通知使用者它不应该睡觉。所以现在想象一个案例,当你的消费者当前正在执行任务A
,任务B
到达时,它看到消费者正在工作,只会进入队列,但它不是一个atomic
操作,所以在这个检查(消费者正忙)和将自己添加到队列之间,消费者已经可以完成任务A
并检查队列, 什么也没看到(因为B
仍然没有添加),然后睡觉,但是B
不知道这一点,会等到最终另一个任务C
来唤醒消费者。
我希望这表明这些问题是不同的,它们有许多不同的解决方法(你可以很容易地谷歌一些方法来解决这些问题)。
例如,在java中,您可以使用BlockingQueue
来解决睡眠理发师问题,因此基本上队列本身将唤醒您的生产者,以防有新任务要执行。
解决生产者-消费者问题的一种方法,也是使用固定大小的BlockingQueue
,所以当queue
满时,生产者将被blocked
,当它会尝试添加更多任务时,直到队列中有更多的可用空间。
这些问题是不同的,有些解决方案只能解决其中一个问题,而不是同时解决两个问题。例如,对于消费者-生产者问题,除了睡眠之外,根据您的要求,还有其他策略:
- 当队列已满时,只需丢弃 Producer 创建的新任务即可。这看起来很愚蠢且效率低下,但是在现实世界中,当您不需要执行所有生成的任务时,有时会使用此方法。一个相当简单的例子是,你有一个创建一些任务的生产者,但这些任务有超时,超时是由一些第三方数据提供者设置的,所以时间甚至在你实际创建任务之前就开始了,所以睡眠不是一种选择,如果队列已满,那么你不太可能及时完成这个任务, 如果你增加队列,不仅这个任务会失败,而且下一个任务也会因超时而失败,最终会发生什么 - 不会有及时执行的任务。因此,您的选择是丢弃一些新任务,以便在某个时候,您可以再次添加它们,并且它们将及时执行。这是一个真实世界的例子,来自银行发送交易的利率报价。
- 解决这个消费者-生产者问题的另一种选择是使用来自响应式编程的命名
reactive streams
。简单地说,当你的消费者真正告诉你的生产者有关其吞吐量的信息时。它可以是他目前准备使用的任务数,也可以是每秒的任务数。例如,您可以查看此实现或本文
这两种方法解决了消费者生产者问题,但它们不能解决睡眠理发师问题,因为它们没有具体说明生产者与任务队列的通信。
我认为你弄错了睡眠理发师问题,因为它不是关于候诊室(队列)满了,而是空的。问题是,如果候诊室是空的,理发师正忙于其他人,如果没有一些同步,你会发现自己处于错误的状态。(在消费者-生产者问题中,你总是处于正确的状态)。特别是新顾客来了,看到理发师很忙,就去了候诊室,想象一下,这个候诊室很远,那么就要花一些时间才能到达那里,当他走到这个房间时,理发师已经完成了他的工作,并用摄像机检查是否有人在这个房间里等着, 但是现在没有人在那里,因为你还在走,所以理发师睡着了,你到达候诊室,永远坐在那里,如果没有其他顾客来叫醒理发师。
还有其他一些方法可以解决此问题。然而,最常见的确实是使用BlockingQueue
.但是,在您看来,BlockingQueue
总是可以解决这两个问题,但它仅适用于 固定大小BlockingQueue
.例如,您也可以在没有任何容量限制的情况下阻塞队列(显然除了您的堆大小),例如LinkedBlockingQueue
,那么生产者将永远不会睡觉,因为队列只会在需要时增加其容量。它也广泛使用的方法。因为有时你无法停止你的生产者,因为它可能是某个远程第三方系统,它永远不会停止,只会给你生成新的任务,你需要消耗所有这些任务,你显然不能对它说停止,因为可能有其他客户端在听这些数据,当只有一个客户端不如其他客户端快时,他们不想停止。因此,您需要使用没有固定大小的队列,因此我们将通过阻塞队列来解决睡眠理发师问题,但我们仍然可能存在吞吐量问题,即消费者生产者问题。为了解决这个问题,我们可以创建一些观察线程,它有时会检查队列的当前大小,如果在某个时候它看到队列大小显着增加,它可以为这个队列创建另一个消费者,或者通知开发人员有问题,或者做一些其他事情。然而,这个观察者根本无法帮助我们解决睡眠理发师的问题。
我需要提一下,这些不是解决这些问题的唯一方法,它们可能有其优点和缺点。
我试图突出显示你可以谷歌完全理解它们的单词。