第五章 稍欠经典的同步问题
5.5 圣诞老人问题
这个问题来自William Stallings的操作系统[11]一书,但他把它归功于佛蒙特州圣迈克尔学院的John Trono。
圣诞老人在北极的商店里睡觉,只能被以下条件之一唤醒:(1)所有九只驯鹿在南太平洋度假回来,或(2)一些精灵制作玩具有困难; 为了让圣诞老人得到睡眠,精灵们只能在三个人遇到问题时叫醒他。 当三个精灵在解决问题时,任何其他希望拜访圣诞老人的精灵都必须等待那些精灵返回。 如果圣诞老人醒来发现三个精灵正在他的商店门口等待,最后一只驯鹿从热带地区回来,圣诞老人决定精灵可以等到圣诞节后,因为让雪橇准备好更重要。 (假设驯鹿不想离开热带地区,因此它们一直呆在那里,直到最后一刻。)到达的最后一头驯鹿必须得到圣诞老人,而其他人则在温暖的小屋里等待,然后才能驾驶雪橇。
以下是一些附加规范:
•第九个驯鹿到来后,圣诞老人必须调用prepareSleigh,然后所有九个驯鹿必须调用getHitched。
•第三个精灵到来后,圣诞老人必须调用helpElves。 同时,所有三个精灵都应该调用getHelp。
•所有三个精灵必须在任何额外的精灵进入之前调用getHelp(增加精灵计数器)。
圣诞老人应该在一个循环中运行,这样他就可以帮助许多精灵。 我们可以假设有9只驯鹿,但可能有许多精灵。
5.5.1 圣诞老人问题提示
elves(精灵)和reindeer(驯鹿)都是计数器,都受互斥锁mutex保护。 精灵和驯鹿得到互斥锁来修改计数器; 圣诞老人检查它们。
圣诞老人等待santaSem信号量,直到精灵或驯鹿向他发出信号。
驯鹿等待reindeerSem信号量,直到圣诞老人发出信号示意他们进入围场并拉车。
当三个精灵得到帮助时,精灵们使用elfTex防止额外的精灵进入。
5.5.2 圣诞老人问题方案
圣诞老人的代码非常简单。 请记住它在循环中运行。
当圣诞老人醒来时,他会检查这两个条件中的哪一个,并与驯鹿或等待的精灵打交道。 如果有九个驯鹿在等待,圣诞老人会调用prepareSleigh,然后向reindeerSem发出九次信号,让驯鹿调用getHitched。 如果有精灵等待,圣诞老人只会调用helpElves。 精灵没有必要等圣诞老人; 一旦他们发出santaSem信号,他们就可以立即调用getHelp。
圣诞老人没有必要减少精灵的数量,因为精灵在外出的路上会这样做。
这是驯鹿的代码:
第九只驯鹿向圣诞老人发出信号,然后加入到其他正在等待reindeerSem的驯鹿。 当圣诞老人发出信号时,驯鹿全部执行getHitched。
精灵代码类似,只是当第三个精灵到达时,它必须禁止后续的精灵到达,直到前三个执行getHelp。
前两个精灵在释放互斥锁的同时释放elfTex,但最后一个精灵拥有elfTex,禁止其他精灵进入,直到所有三个精灵都调用了getHelp。
最后一个离开的精灵释放elfTex,允许下一批精灵进入。