基于信号量的进程同步与互斥(3)
基于信号量的进程同步与互斥(3)
1. 吸烟者问题
涉及代理人和三个吸烟者。 吸烟者永远地循环,首先等待配料,然后制作和吸烟。配料的成分是烟草,纸和火柴。我们假设代理商拥有无限供应的所有三种成分,每个吸烟者都有无限供应的其中一种成分; 也就是说,一个吸烟者有火柴,另一个有纸,第三个有烟草。代理商反复随机地选择两种不同的成分,并使其可供吸烟者使用。根据选择的成分,具有补充成分(剩下那种成分)的吸烟者应该拿走这两种资源并继续处理(制作和抽烟)。例如,如果代理商拿出烟草和纸张,那么拥有火柴的吸烟者应该拿起两种成分,制作香烟,然后发出信号给代理商。
可以理解为代理商是分配资源的操作系统,吸烟者是需要资源的应用程序,要确保:如果资源可用,允许再多执行一个应用程序;相反如果应用程序无法继续,需要避免唤醒它。另外,需要添加限制,不能修改代理代码,如果代理表示操作系统,不希望每次新应用程序出现时都修改代理。
信号量:
agentSem = Semaphore(1)
tobacco = Semaphore(0)
paper = Semaphore(0)
match = Semaphore(0)
代理商需要三个并发线程,分别代表提供其最终的两种配料。
Agent A: //提供烟草和纸的代理商
while true:
P(agentSem)
V(tobacco)
V(paper)
Agent B: //提供纸和火柴的代理商
while true:
P(agentSem)
V(paper)
V(match)
Agent C: //提供火柴和烟草的代理商
while true:
P(agentSem)
V(match)
V(tobacco)
需要引入三个Pusher的辅助线程和附加变量和信号量,比如isTobacco表示烟草是否已经供应,tobaccoSem实现Pusher和Smokers with tobacco的同步,也就是去唤醒只有tobacco的吸烟者,其他同理。
isTobacco = isPaper = isMatch = False
tobaccoSem = Semaphore(0)
paperSem = Semaphore(0)
matchSem = Semaphore(0)
Pusher A:
while true:
P(tobacco)
P(mutex)
if isPaper:
isPaper = False
V(matchSem)
elif isMatch:
isMatch = Flase
V(paperSem)
else:
isTobacco = True
V(mutex)
Pusher B:
while true:
P(paper)
P(mutex)
if isTobacco:
isTobacco = False
V(matchSem)
elif isMatch:
isMatch = Fales
V(tobaccoSem)
else:
isPaper = True
V(mutex)
Pusher C:
while true:
P(match)
P(mutex)
if isTobacco:
isTobacco = False
V(paperSem)
elif isPaper:
isPaper = False
V(tobaccoSem)
else:
isMatch = true
V(mutex)
Smoker with tobacco:
while true:
P(tobaccoSem)
makeCigarette()
V(agentSem)
smoke()
Smoker with paper:
while true:
P(paperSem)
makeCigarette()
V(agentSem)
smoke()
Smoker with match:
while true:
P(smokeSem)
makeCigarette()
V(agentSem)
smoke()
如果代理商不等待吸烟者,则需要用整数来计算桌子上烟草、纸和火柴的数量。
numTobacco = numPaper = numMatch = 0
Pusher A:
while true:
P(tobacco)
P(mutex)
if numPaper > 0:
numPaper -= 1
V(matchSem)
elif numMatch > 0:
numMatch -= 1
V(paperSem)
else:
numTobacco += 1
V(mutex)
2. 理发师问题
理发店有一位理发师、一把理发椅和n把供等候理发的乘客坐的椅子;如果没有顾客,理发师便在理发椅上睡觉,当一个顾客到来时,叫醒理发师;如果理发师正在理发时,又有顾客来到,则如果有空椅子可坐,就坐下来等待,否则就离开。
理发师等待customer信号,直到顾客进入理发店,然后顾客等待barber信号,直到理发师发出信号让他坐下来。理发后,顾客发出customerDone信号,并等待理发师的barberDone信号。
int customer = 0;
semaphore mutex = 1; //互斥访问customer
semaphore customer = 0;
semaphore barber = 0;
semaphore customerDone = 0;
semaphore barberDone = 0;
Process Barber
while (true) {
P(customer);
V(barber);
curHair();
P(customerDone);
V(barberDone);
}
Process Customer
P(mutex);
if (customers == n + 1) {
V(mutex);
} else {
customers += 1;
V(mutex);
V(customer);
P(barber);
getHairCut();
V(customerDone);
P(barberDone);
P(mutex);
customers -= 1;
V(mutex);
}
如果要实现理发店的先来先服务的话,需要引入queue。并且设当前线程为self
,如果编写self.sem = Semaphore(0)
,则每个线程都会获得自己的信号量
customers = 0
mutex = Semaphore(1)
customer = Semaphore(0)
customerDone = Semaphore(0)
barberDone = Semaphore(0)
queue = []
Process Customer
self.sem = Semaphore(0)
P(mutex)
if customers == n + 1:
V(mutex)
else:
customer += 1
queue.append(self.sem)
V(mutex)
V(customer)
P(self.sem)
getHairCut()
V(customerDone)
P(barberDone)
P(mutex)
customers -= 1
V(mutex)
Process Barber
P(customer)
P(mutex)
sem = queue.pop(0)
V(mutex)
cutHair()
P(customerDone)
V(barberDone)
4. 圣诞老人问题:
圣诞老人在北极的商店里睡觉,只能被以下条件之一唤醒:(1)所有九只驯鹿在南太平洋度假回来,或(2)一些精灵制作玩具有困难; 为了让圣诞老人得到睡眠,精灵们只能在三个人遇到问题时叫醒他。 当三个精灵在解决问题时,任何其他希望拜访圣诞老人的精灵都必须等待那些精灵返回。 如果圣诞老人醒来发现三个精灵正在他的商店门口等待,最后一只驯鹿从热带地区回来,圣诞老人决定先和驯鹿去准备雪橇,精灵们可以在商店里等待圣诞老人。
附加规范:
- 第九个驯鹿到来后,圣诞老人必须调用prepareSleigh,然后所有九个驯鹿必须调用getHitched。
- 第三个精灵到来后,圣诞老人必须调用helpElves。 同时,所有三个精灵都应该调用getHelp。
- 所有三个精灵必须在任何额外的精灵进入之前调用getHelp(增加精灵计数器)。
- 圣诞老人应该在一个循环中运行,这样他就可以帮助许多精灵。 我们可以假设有9只驯鹿,但可能有许多精灵。
elves = 0
reindeer = 0
santaSem = Semaphore(0)
reindeerSem = Semaphore(0)
elfTex = Semaphore(1) //防止额外的精灵进入
mutex = Semaphore(1) //互斥访问elves和reindeer
Process Reindeer
P(mutex)
reindeer += 1
if reindeer == 9:
V(santaSem)
V(mutex)
P(reindeerSem)
getHitched()
Process elfTex
//前两个精灵在释放互斥锁的同时释放elfTex,但最后一个精灵拥有elfTex,禁止其他精灵进入,直到所有三个精灵都调用了getHelp。最后一个离开的精灵释放elfTex,允许下一批精灵进入
P(elfTex)
P(mutex)
elves += 1
if elves == 3:
V(santaSem)
else:
V(elfTex)
V(mutex)
getHelp()
P(mutex)
elves -= 1
if elves == 0:
V(elfTex)
V(mutex)
Process Santa
P(santaSem)
P(mutex)
if reindeer >= 9:
prepareSleigh()
for i in range(9):
V(reindeerSem)
reindeer -= 9
elif elves == 3:
helpElves()
V(mutex)
5. 过河问题
在华盛顿州雷德蒙德附近,有一艘划艇,Linux黑客和微软员工都用它来过河。渡船能容纳四人,不会离岸。为了保证乘客的安全,不允许把一个黑客加三个员工或者一个员工加三个黑客放在船上。其他任何组合都是安全的。当每个线程登上船时,它应该调用一个叫做board的函数。 您必须保证船上的所有四个线程都要在下一次船载的任何线程之前调用board。在所有四个线程都调用了board之后,其中只有一个要调用一个名为rowBoat的函数,表明该线程将拿桨划船。 是哪一个线程调用该函数并不重要,只要有一个调用了即可。
该题是H20问题的变形。以下只给出了黑客线程,员工线程对称。
barrier = Barrier(4)
mutex = Semaphore(1)
hackers = 0
serfs = 0
hackerQueue = Semaphore(0)
serfQueue = Semaphore(0)
local isCaptain = False //local是局部变量,使最后一个登船的线程调用rowBoat
Process Hacker
P(mutex)
hackers += 1
if hackers == 4:
V(hackerQueue)
V(hackerQueue)
V(hackerQueue)
V(hackerQueue)
hackers -= 4
isCaptain = True
elif hackers == 2 and serfs >= 2:
V(hackerQueue)
V(hackerQueue)
V(serfQueue)
V(serfQueue)
serfs -= 2
hackers -= 2
isCaptain = True
else:
V(mutex)
P(hackerQueue)
board()
barrier.wait()
if isCaptain:
rowBoat();
V(mutex)
6. 餐厅问题
学生们在餐厅里用餐(dine),然后离开(leave)。 在调用dine之后,调用leave之前,学生被认为“准备离开”。适用于学生的同步约束是,为了保持社会温和的错觉,学生永远不能独自坐在桌子旁。 如果在完成用餐之前一同用餐的其他所有人都已离开,则该学生被认为是独自坐着。
分析题目只有一种情况需要等待:一个学生正在吃饭,另一个学生准备离开。要解决这种现象,有两种方法:(1)等另外一个学生来吃饭;(2)等正在吃饭的同学一起吃完离开。
eating = 0
readyToLeave = 0
mutex = Semaphore(1)
okToLeave = Semaphore(0)
getFood()
P(mutex)
eating += 1
if eating == 2 and readyToLeave == 1:
V(okToLeave)
readyToLeave -= 1
V(mutex)
dine()
P(mutex)
eating -= 1
readyToLeave += 1
if eating == 1 and readyToLeave == 1:
V(mutex)
P(okToLeave)
elif eating == 0 and readyToLeave == 2:
V(okToLeave)
readyToLeave -= 2
V(mutex)
else:
readyToLeave -= 1
V(mutex)
leave()
餐厅问题进阶版
题目来自https://blog.csdn.net/booksyhay/article/details/82692362