信号量设计题归纳

独木桥问题

题:请用信号量解决以下的“过独木桥”问题:同一方向的行人可连续过桥,当某一方向有人过桥时,另一方向的行人必须等待;当某一方向无人过桥时,另一方向的行人可以过桥。

在这里插入图片描述
答:信号量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)
}

参考原文链接

  • 2
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小龙凤团茶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值