第四章经典同步问题 4.5吸烟者问题 吸烟者问题问题最初由Suhas Patil [8]提出,他声称用信号量无法解决。 这种说法带有一定的条件,但无论如何,问题是有趣且具有挑战性的。 涉及四个线程:代理人和三个吸烟者。 吸烟者永远地循环,首先等待配料,然后制作和吸烟。配料的成分是烟草,纸和火柴。 我们假设代理商拥有无限供应的所有三种成分,每个吸烟者都有无限供应的其中一种成分; 也就是说,一个吸烟者有火柴,另一个有纸,第三个有烟草。 代理商反复随机地选择两种不同的成分,并使其可供吸烟者使用。根据选择的成分,具有补充成分(剩下那种成分)的吸烟者应该拿走这两种资源并继续处理(制作和抽烟)。 例如,如果代理商拿出烟草和纸张,那么拥有火柴的吸烟者应该拿起两种成分,制作香烟,然后发出信号给代理商。 为了解释这个前提,代理表示分配资源的操作系统,而吸烟者表示需要资源的应用程序。 问题是要确保:如果资源可用,允许再多执行一个应用程序,那么应该唤醒这些应用程序。 相反,如果应用程序无法继续,我们希望避免唤醒它。 基于这个前提,这个问题有三个版本经常出现在教科书中: 不可能版本:Patil的版本对解决方案施加了限制。首先,您不能修改代理代码。 如果代理表示操作系统,则假设您不希望每次新应用程序出现时都修改它,这是有意义的。 第二个限制是您不能使用条件语句或信号量数组。 有了这些限制,问题就无法解决,但正如Parnas所指出的那样,第二个限制是非常人为的[7]。 由于这些限制,许多问题变得无法解决。 有趣的版本:此版本保留了第一个限制 - 您无法更改代理代码 - 但它会丢弃其他代码。 平凡的版本:在一些教科书中,问题指出代理人应该根据可用的成分发信号通知下一个应该接下来的吸烟者。 这个版本的问题是无趣的,因为它使整个前提,成分和香烟,都无关紧要。 此外,实际上,要求代理知道线程以及它们正在等待的内容可能不是一个好主意。 最后,这个版本的问题太容易了。 当然,我们将专注于有趣的版本。 要完成问题陈述,我们需要指定代理代码。 代理使用以下信号量: 代理实际上由三个并发线程组成,代理A,代理B和代理C.每个代理都在agentSem上等待; 每当agentSem发出信号时,其中一个代理就会通过发出两个信号量的信号来唤醒并提供成分。 这个问题很难,因为自然解决方案不起作用。 很容易写出这样的东西: 这个解决方案有什么问题? 4.5.1死锁-6# 先前解决方案的问题是存在死锁的可能性。 想象一下,代理商推出了烟草和纸张。 由于有火柴的吸烟者正在等待烟草,因此可能会被解除阻止。 但是有烟草的吸烟者正在等待纸张,因此有可能(甚至可能)它也会被解除阻止。 然后第一个线程将在纸张上阻塞,第二个线程将在火柴上阻塞。死锁! 4.5.2吸烟者问题提示 Parnas提出的解决方案使用三个称为“贩毒者(pushers)”的辅助线程,响应来自代理的信号,跟踪可用的成分,并给相应的吸烟者发送信号。 附加变量和信号量是 布尔变量表示该成分是否在桌子上。贩毒者使用烟草来向吸烟者发出烟草信号,同样也使用其他信号量。 4.5.3吸烟者问题解决方案 以下是其中一个贩毒者的代码: 只要桌子上有烟草,这个贩毒者就会醒来。 如果它发现isPaper为真,它知道Pusher B已经运行,所以它向拥有火柴的吸烟者发出信号。 同样,如果它在桌子上找到火柴,它就向拥有纸张的吸烟者发出信号。 但如果Pusher A先运行,那么它会发现isPaper和isMatch的值都是假。 它不能向任何吸烟者发出信号,所以它设置isTobacco标志为真。 其他贩毒者是相似的。 由于贩毒者完成所有实际工作,吸烟者代码是微不足道的: Parnas提出了一个类似的解决方案,它将布尔变量按位组合成一个整数,然后使用整数作为信号量数组的索引。 这样他就可以避免使用条件(人为约束之一)。 结果代码更简洁,但其功能并不那么明显。 4.5.4广义吸烟者问题 Parnas建议,如果我们修改代理商,消除代理商在推出配料后等待的要求,那么吸烟者问题会变得更加困难。 在这种情况下,桌子上可能存在多个成分实例。 思考:修改以前的解决方案来处理这种变化。 4.5.5广义吸烟者问题提示 如果代理商不等待吸烟者,成分可能会积聚在桌子上。 我们需要使用整数来计算它们,而不是使用布尔值来跟踪成分。 4.5.6广义吸烟者问题方案 以下是Pusher A修改后的代码: 可视化此问题的一种方法是想象当代理运行时,它会创建两个贩毒者,为他们每人提供一个成分,并将它们放在一个包含所有其他贩毒者的房间中。 由于互斥锁,贩毒者进入一个房间,那里有三个沉睡的吸烟者和一张桌子。 每个贩毒者进入房间并检查桌子上的成分,每次一个。如果他可以组装出一套完整的成分,他会把它们从桌子上取下来并唤醒相应的吸烟者。 如果没有,他将他的成分留在桌子上,离开时不会吵醒任何人。 这是我们将多次看到的模式示例,我将其称为记分板(scoreboard)。变量numPaper,numTobacco和numMatch跟踪系统的状态。 当每个线程提出通过互斥锁时,它会检查状态,就像查看记分板一样,并做出相应的反应。 (责任编辑:admin) |