生产者消费者与睡眠理发师之间的区别

时间:2018-04-20 08:28:17

标签: concurrency synchronization race-condition

我正在学习同步问题并阅读Producer Consumer problemSleeping Barber problem

我发现生产者消费者问题与睡眠理发师问题非常相似。坦率地说,我找不到它们之间的区别。

让我说...... 当制作人制作产品时,他将其添加到队列中;当顾客到达理发店时,他去了候诊室。 当然,如果他正在睡觉,顾客会去理发店并叫醒他。它在产品消费者问题上似乎相似。如果队列为空,消费者可能会睡觉,并且有人应该在将新产品添加到队列时唤醒他。

当队列已满时,生产者不应该生产更多产品,因此他睡得更好。当消费者从队列中消费产品并且队列中有一个位置时,生产者应该醒来(或者应该被某人唤醒)才能工作。 我认为这与睡眠理发师问题类似。等候室满员时,新来的客户可以在候诊室等候。当候诊室里有人去理发店时,外面的顾客可以进入候诊室。 (当然,如果等候室里没有空椅子,顾客可以回家,但我不认为这是一个很大的区别)

我认为解决这两个问题的实现是相似的。在各种版本的实现中,我看到了使用两个信号量和一个互斥量的实现。两个信号量用于唤醒睡眠演员,并使用互斥锁来防止数据区域因并发访问而损坏。 我相信这个解决方案可以解决这两个问题。所以我觉得生产者消费者问题和睡眠理发师问题没有区别。

1 个答案:

答案 0 :(得分:1)

生产者 - 消费者问题是关于生产者和消费者有不同的throughputs,所以在生产者生产任务的情况下,然后消费者执行它们,然后它们之间的任务的队列大小将增长,因此将增长任务从到达队列的那一刻起,以及消费者从队列中获取任务的时间,最终你将失去记忆。

睡觉理发师问题大概是race conditions。想象一下,你有相同的Producer生成任务(人们来到理发店)和消费者(理发师)。如果队列中没有任何更多的任务,那么你的消费者不会进行busy waiting休眠,所以当新任务到达时,它首先通知消费者它不应该睡觉。所以现在假设一个案例,当Consumer当前正在执行任务A,并且任务B到达时,它会看到Consumer正在运行并且只会进入队列,但它不是{{1}操作,所以在此检查(消费者忙)和将自己添加到队列之间,消费者可以完成任务atomic并检查队列,看不到任何内容(因为A仍未添加),并且进入睡眠状态,但是B并不知道这一点,并且会等到最终另一个任务B到来并唤醒消费者。

我希望这表明这些问题是不同的,并且它们有许多不同的方法来解决它们(您可以轻松地通过某些方式来解决它们)。

例如在java中,你可以使用C来解决睡眠理发问题,所以基本上队列本身会唤醒你的制作人,以防有新任务被执行。

解决生产者 - 消费者问题的一种方法是使用固定大小的BlockingQueue,因此当BlockingQueue已满时,生产者将queue,当它尝试时添加更多任务,直到队列中有更多可用空间。

这些问题不同,有些解决方案只解决其中一个,而不是两者。例如,对于消费者 - 生产者问题,除了睡觉,根据您的要求,还有其他策略:

  • 当队列已满时,只丢弃生产者创建的新任务。这看似愚蠢和低效,但在现实世界中,当您不需要执行所有生成的任务时,有时会使用此方法。一个相当简单的例子可能是你有一个创建一些任务的生产者,但是这些任务有超时和超时是由某个第三方数据提供者设置的,所以时间甚至在消费者实际创建任务之前就开始了,所以睡觉不是一个选择,如果队列已满,那么你不太可能及时完成这项任务,如果你要增加队列,不仅这个任务会失败,而且下一个任务也会因超时而失败,最终会发生什么 - 那里将不是及时执行的任务。因此,您可以选择放弃一些新任务,以便在某些时候,您可以再次添加它们,并且它们将会及时执行。它是一个真实世界的例子,从银行发送交易价格的报价开始。
  • 解决此消费者 - 生产者问题的另一个选择是使用来自Reactive编程的名为blocked的名称。简单地说,当您的消费者实际告诉您的生产者有关其吞吐量的信息时它可以是他现在准备消耗的任务数,也可以是每秒任务数。例如,您可以查看此implementation或此article

这两种方法解决了消费者生产者问题,但它们并没有解决睡眠理发师问题,因为他们没有具体说明生产者与任务队列的沟通。

我认为你睡觉的理发师问题是错误的,因为它不是关于等候室(队列)已满,而是空的。问题是,如果等候室是空的并且理发师忙于其他人,没有一些同步,你会发现自己处于错误的状态。(在消费者 - 生产者问题中,你总是处于正确的状态)。特别是新客户到了,看到理发师忙着去候车室,想象一下,这个等候室很远,然后到那儿需要一些时间,当他走到这个房间时,理发师已经完成了他的工作,并用一台摄像机检查是否有人在这个房间里等,但此刻没有人,因为你还在,所以理发师睡着了,你到了候诊室,坐在那里,永远,如果没有其他顾客会来唤醒一个理发师。

还有其他一些方法可以解决这个问题。然而,最常见的确是使用reactive streams。但是,您可能认为BlockingQueue始终可以解决这两个问题,但仅适用于固定大小BlockingQueue。您也可以使用例如阻塞队列而没有任何容量限制(显然除了堆大小),例如BlockingQueue,然后生产者永远不会睡觉,因为队列只会在需要时增加容量。它也是广泛使用的方法。因为有时你不能阻止你的制作人,因为它可能是一些远程的第三方系统,它永远不会停止并且只产生你的新任务,而你需要消耗所有这些系统,你显然不能说它可以停止,因为可能有其他客户收听这些数据,并且他们不希望在只有一个客户端没有其他客户端时停止。因此,您需要使用没有固定大小的队列,因此我们将通过阻塞队列解决睡眠理发问题,但我们仍然可以解决吞吐量问题,这意味着消费者 - 生产者问题。为了解决它,我们可以创建一些观察者线程,它有时会检查队列的当前大小,如果在某些时候它看到队列大小显着增加它可以例如为此队列创建另一个消费者,或者通知开发人员那里是一个问题,或做一些其他的事情。然而,这位观察者并没有帮助我们解决睡眠理发师的问题。

我需要提一下,这些并不是解决这些问题的唯一方法,而且它们各有利弊。

我尝试突出显示您可以谷歌完全理解的词语。