独木桥问题
题:请用信号量解决以下的“过独木桥”问题:同一方向的行人可连续过桥,当某一方向有人过桥时,另一方向的行人必须等待;当某一方向无人过桥时,另一方向的行人可以过桥。
答:信号量brigde表示独木桥互斥访问,初值为1;变量eastcount和westcount分别表示从东西方上独木桥的行人数量,初值为0,信号量eastmutex和westmutex是对变量eastcount和westcount数据读写的保护,初值为1,当某个方向上独木桥的人数大于1时,直接上桥,当该方向在桥上的人数为0时释放bridge锁。
bridge = semaphore(1)
eastmutex = semaphore(1)
westmutex = semaphore(1)
var eastcount = 0
var westcount = 0
east:
while(true):
P(eastmutex)
eastcount ++
if(eastcount == 1):
P(bridge)
V(eastmutex)
上桥
下桥
P(eastmutex)
eastcount --
if(eastcount == 0):
V(bridge)
V(eastmutex)
west:
while(true):
P(westmutex)
westcount ++
if(westcount == 1):
P(bridge)
V(westmutex)
上桥
下桥
P(westmutex)
westcount --
if(westcount == 0):
V(bridge)
V(westmutex)
理发师问题
理发店有一位理发师、一把理发椅和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) { // n把等候椅子+1把理发椅已经坐满,不进入理发店
V(mutex);
} else {
customers += 1; // 进入理发店
V(mutex);
V(customer);
P(barber); // 等待理发师信号
getHairCut();
V(customerDone);
P(barberDone);
P(mutex);
customers -= 1;
V(mutex);
}
圣诞老人问题:
圣诞老人在北极的商店里睡觉,只能被以下条件之一唤醒:(1)所有九只驯鹿在南太平洋度假回来,或(2)一些精灵制作玩具有困难; 为了让圣诞老人得到睡眠,精灵们只能在三个人遇到问题时叫醒他。 当三个精灵在解决问题时,任何其他希望拜访圣诞老人的精灵都必须等待那些精灵返回。 如果圣诞老人醒来发现三个精灵正在他的商店门口等待,最后一只驯鹿从热带地区回来,圣诞老人决定先和驯鹿去准备雪橇,精灵们可以在商店里等待圣诞老人。
附加规范:
第九个驯鹿到来后,圣诞老人必须调用prepareSleigh,然后所有九个驯鹿必须调用getHitched。
第三个精灵到来后,圣诞老人必须调用helpElves。 同时,所有三个精灵都应该调用getHelp。
所有三个精灵必须在任何额外的精灵进入之前调用getHelp(增加精灵计数器)。
圣诞老人应该在一个循环中运行,这样他就可以帮助许多精灵。 我们可以假设有9只驯鹿,但可能有许多精灵。
elves = 0 // 精灵数量(保证<=3)
reindeer = 0 // 驯鹿数量(<=9)
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)
生产者消费者问题
某银行有n个服务柜台。每个顾客进店后先取一个号,并且等待叫号。当一个柜台人员空闲下来时,就叫下一个号。试设计一个使柜台人员和顾客同步的算法。
int waiting_id = 0; //当前客户编号
semaphore waiting_mutex = 1; //对waiting_id互斥访问
int next_id = 0; //下一位要服务客户编号
semaphore next_mutex = 1; //对next_id互斥访问
customer:
P(waiting_mutex)
waiting_id ++
V(waiting_mutex)
sever:
while(true):
P(next_mutex)
if(waiting_id > next):
next_id ++
V(next_mutex)
sever()
系统中有多个生产者进程和消费者进程,共享用一个可以存 1000 个产品的缓冲区(初始为空),当缓冲区为未满时,生产者进程可以放入一件其生产的产品,否则等待;当缓冲区为未空时,消费者进程可以取走一件产品,否则等待。要求一个消费者进程从缓冲区连续取出 10 件产品后,其他消费者进程才可以取产品,请用信号量 P,V 操作实现进程间的互斥和同步,要求写出完整的过程;并指出所用信号量的含义和初值。
product buffer[1000]
i = 0
j = 0
empty 1000
full 0
mutex1 0 //用于消费者之间互斥
mutex2 0 //用于生产者之间互斥
producer{
while(true){
p(empty)
x = makeproduct()
p(mutex1)
buffer[i] = x
i = (i+1)%1000
v(mutex1)
v(full)
}
}
user{
while(true){
p(mutex2)
for(int t = 0;t<10 ;t++){
p(full)
consume(buffer[j])
j = (j+1)%1000
v(empty)
}
v(mutex2)
}
}
某银行有n个服务柜台。每个顾客进店后先取一个号,并且等待叫号。当一个柜台人员空闲下来时,就叫下一个号。试设计一个使柜台人员和顾客同步的算法。
var:
waiting_id = 0
next_id = 0
semaphore:
mutex1 = 1//柜台之间互斥
mutex2 = 1//取号者互斥
sever{
while(true){
p(mutex1)
while(next_id > waiting_id);
call(waiting_id)
next_id ++
v(mutex1)
sever()
}
}
people{
p(mutex2)
waiting_id ++
v(mutex2)
}
对共享资源的读写操作,任一时刻“写者”最多只允许一个,而“读者”则允许多个,“读-写”互斥,“写-写”互斥,“读-读”允许。读者算法的模式:第一个读线程加锁,最后一个读线程解锁。
读者优先,即当有一个读者未读完时,同时在排队的写者和读者,读者都可以直接进入房间读书,而写者必须等所有读者出来后才可以进房间
var:
readers = 0 //读者数量
semaphore:
roommutex = 1
mutex = 1 //对readers变量的保护
writer{
p(roommutex)
write()
v(roommutex)
}
reader{
p(mutex)
readers ++
if(readers == 1){
p(roommutex)
}
v(mutex)
read()
p(mutex)
readers --
if(readers == 0){
v(roommutex)
}
v(mutex)
leave()
}
读写公平
即严格按照排队的顺序让写者和读者进房间,只需添加一个闸机,当写者位于队首时会给闸机上锁,使得后面的读者无法进入房间,等到房间里的读者出来后,写者就可进入
var:
readers = 0 //读者数量
semaphore:
roommutex = 1
mutex = 1 //对readers变量的保护
turnstile = 1
writer{
p(turnstile)
p(roommutex)
v(turnstile)
write()
v(roommutex)
}
reader{
p(turnstile)
v(turnstile)
p(mutex)
readers ++
if(readers == 1){
p(roommutex)
}
v(mutex)
read()
p(mutex)
readers --
if(readers == 0){
v(roommutex)
}
v(mutex)
leave()
}
写者优先 最难
int readers, writers = 0;
semaphore mutexW = 1; //保护对writes的访问
semaphore mutexR = 1; //保护对readers的访问
semaphore wMutex = 1; //当有读者或写者进行操作时,阻止其他写者进行操作
semaphore rMutex = 1; //当有写者进行操作时,阻止其它读者进行操作
semaphore MUTEX = 1; //在写者优先算法中,这个信号量很重要,目的是在rMutex上不允许建设长队列,我们可以设想一种情况:如果没有该信号量,则当写进程访问时,所有的读进程都等待在rMutex的队列上;当写进程结束访问,开始处理第一个读进程(readers++),但此时有写进程也准备访问,根据写者优先的要求,应该先执行写进程,但此时写进程会等待在rMutex的队列上,与准备访问(还没处理)的读进程(可能有成百上千个)共同竞争读进程释放的rMutex,结果可想而知。所以只允许一个进程在rMutex上排队,其他进程在等待rMutex之前,在MUTEX上排队。
writer{
p(mutexW)
writers ++
if(writers == 1){
P(rMutex) // 还要有写者,那么队列中的读者都无法进屋
}
v(mutexW)
p(wMutex)
write()
v(wMutex)
p(mutexW)
writers --
if(writers == 0){
v(rMutex)
}
v(mutexW)
}
reader{
p(MUTEX)
p(rMutex)
p(mutexR)
readers ++
if(readers == 1){
p(wMutex)
}
v(mutexR)
v(rMutex)
v(MUTEX) //当读进程和写进程同时等待时,此时读进程在MUTEX队列等待,而写进程在rMutex等待,rMutex先释放,所以写进程会先进入房间,若没有写进程了,则读进程会进入一个到MUTEX队列中
read()
p(mutexR)
readers --
if(readers == 0){
v(wMutex)
}
v(wutexR)
}
寿司店问题。假设一个寿司店有 5 个座位,如果你到达的时候有一个空座位,你可以立刻就坐。但是如果你到达的时候 5 个座位都是满的有人已经就坐,这就意味着这些人都是一起来吃饭的,那么你需要等待所有的人一起离开才能就坐。编写同步原语,实现这个场景的约束。
var:
set = 5 //五个空座位
semaphore:
mutex = 1 //保护set
MUTEX = 1 //按顺序进入店
sets = 5 //座位互斥
me{
p(MUTEX)
p(mutex)
if(set == 5){
PS(sets,5,1)
v(mutex)
}
else{
PS(sets,1,1)
v(mutex)
}
p(mutex)
set --
v(mutex)
v(MUTEX)
est()
p(mutex)
set ++
v(mutex)
VS(sets,1)
}
搜索-插入-删除问题。三个线程对一个单链表进行并发的访问,分别进行搜索、插入和删除。搜索线程仅仅读取链表,因此多个搜索线程可以并发。插入线程把数据项插入到链表最后的位置;多个插入线程必须互斥防止同时执行插入操作。但是,一个插入线程可以和多个搜索线程并发执行。最后,删除线程可以从链表中任何一个位置删除数据。一次只能有一个删除线程执行;删除线程之间,删除线程和搜索线程,删除线程和插入线程都不能同时执行。
var:
int i = 0
int s = 0
semaphore:
NoSearchMutex = 1
NoInsertMutex = 1
InsertMutex = 1
mutexi = 1
mutexj = 1
delete{
P(NoSearchMutex)
P(NoInsertMutex)
delete()
V(NoSearchMutex)
V(NoInsertMutex)
}
search{
p(mutexs)
s ++
if(s == 1){
p(NoSearchMutex)
}
v(mutexs)
search()
p(mutexs)
s --
if(s == 0){
v(NoSearchMutex)
}
v(mutexs)
}
insert{
p(mutexi)
i ++
if(i == 1){
p(NoInsertMutex)
}
v(mutex)
p(imutex)
insert()
v(imutex)
p(mutexi)
i --
if(i == 0){
v(NoInsertMutex)
}
v(mutexi)
}