特性

  • 缓冲区是临界区,需要用 mutex 夹起来。

1. 生产者消费者问题

例题

桌子上有一个盘子,每次只能向其中放入一个水果。爸爸专向盘子中放苹果,妈妈专向盘子中放橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时,爸爸或妈妈才可向盘子中放一个水果; 仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出。

semaphore plate = 1;
semaphore apple = 0;
semaphore orange = 0;
 
dad(){
	while(1){
		prepare an apple;
		P(plate);
		put the apple to the plate;
		V(apple);
	}
}
 
mom(){
	while(1){
		prepare an orange;
		P(plate);
		put the orange to the plate;
		V(orange);
	}
}
 
son(){
	while(1){
		P(apple);
		take the apple from the plate;
		V(plate);
		eat the apple;
	}
}
 
daughter(){
	while(1){
		P(orange);
		take the orange from the plate;
		V(plate);
		eat the orange;
	}
}
 

2009 年真题

三个进程 P 1、P 2、P 3 互斥使用一个包含 N(N>0)个单元的缓冲区。 P 1 每次用 produce () 生成一个正整数并用 put () 送入缓冲区某一空单元中; P 2 每次用 getodd () 从该缓冲区中取出一个奇数并用 countodd () 统计奇数个数; P 3 每次用 geteven () 从该缓冲区中取出一个偶数并用 counteven () 统计偶数个数。 请用信号量机制实现这三个进程的同步与互斥活动,并说明所定义的信号量的含义。要求用伪代码描述。

semaphore even = 0;
semaphore odd = 0;
semaphore empty = N;
semaphore mutex = 1;
 
p1(){
	while(1){
		x=produce();
		P(empty);
		P(mutex);
		if(x%2==0) V(even);
		else V(odd);
		put();
		V(mutex);
	}
}
 
p2(){
	while(1){
		P(odd);
		P(mutex);
		getodd();
		V(mutex);
		V(empty);
		countodd();
	}
}
 
p3(){
	while(1){
		P(even);
		P(mutex);
		geteven();
		V(mutex);
		V(empty);
		counteven();
	}
}

2014 年真题

系统中有一个能容纳 1000 件产品的环形缓冲区,初始为空。生产者在缓冲区未满时放入产品,否则等待;消费者在缓冲区未空时取出产品,且一个消费者必须连续取出 10 件产品后,其他消费者才能开始取产品。

semaphore mutex1 = 1;//生产者的互斥信号量
semaphore mutex2 = 1;//消费者的互斥信号量
semaphore empty = 1000;
semaphore full = 0;
 
producer(){
	while(1){
		produce sth;
		P(empty);
		P(mutex1);
		put product to buffer area;
		V(mutex1);
		V(full);
	}
}
 
consumer(){
	while(1){
		P(mutex2);
		for(int i=0;i<10;i++){
			P(full);
			take a product from buffer area;
		  	V(empty);
		}
		V(mutex2);
		consume;
	}
}
 

2. 理发师问题

2011 年真题

某银行提供 1 个服务窗口和 10 个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务。

semaphore empty = 10;
semaphore full = 0;
semaphore service = 0;
semaphore mutex = 1;
 
process customer(){
	P(empty);//等待空位
	P(mutex);
	从取号机获得一个号码;
	V(mutex);
	V(full);//通知营业员有新顾客
	P(service);//等待叫号
	V(empty);//叫到号后移动到服务窗口,空出当前位置
	获得服务;
}
	process 营业员
{
	while (TRUE)
	{
		P(full);
		V(service);//叫号
		为顾客服务;
	}
}
 

3. 读者写者问题

4. 哲学家进餐问题

2019 年真题

有n(n≥3)位哲学家围坐在一张圆桌边,每位哲学家交替地就餐和思考。在圆桌中心有 m(m≥1)个碗,每两位哲学家之间有一根筷子。每位哲学家必须取到一个碗和两侧的筷子后,才能就餐,进餐完毕,将碗和筷子放回原位,并继续思考。为使尽可能多的哲学家同时就餐,且防止出现死锁现象。

semaphore chopsticks[n];//下标为i的筷子为第i位哲学家左手边的筷子
semaphore bowl = m;
semaphore limit = min(m,n-1);//保证筷子和碗都充足
 
for(int i=0;i<n;i++){
	chopsticks[i]=1;
}
 
thinker_i(int i){
	P(limit);
	P(chopsticks[i]);
	P(chopsticks[(i+1)%5]);
	吃饭;
	V(chopsticks[(i+1)%5]);
	V(chopsticks[i]);
	V(limit);
	思考;
}

5. 单纯的同步问题

2013 统考真题

某博物馆最多可容纳 500 人同时参观,有一个出入口,该出入口一次仅允许一个人通过。

semaphore mutex = 1;
semaphore room = 500;
visitor()  {
	P(room);
	P(mutex);
	进门;  
	V(mutex);
	参观;  
	P(mutex);
	出门; 
	V(mutex);
	V(room);
}  

2015 年真题

有A、B两人通过信箱进行辩论,每个人都从自口的信箱中取得对方的问题,将答案和向对方提出的新问题组成一个邮件放人对方的信箱中。假设A的信箱最多放M个邮件,B的信箱最多放 N个邮件。初始时A的信箱中有x个邮件,B 的信箱中有 y 个邮件。辩论者每取出一个邮件,邮件数减一。

semaphore fulla = x;
semaphore fullb = y;
semaphore emptya = M-x;
semaphore emptyb = N-y;
semaphore mutexa = 1;
semaphore mutexb = 1;
A(){
	while(1){
		P(fulla);
		P(mutexa);
		从A的信箱取出一个邮件;
		V(mutexa);
		V(emptya);
		回答问题并提出一个新问题;
		P(emptyb);
		P(mutexa);
		将新邮件放入B的邮箱;
		V(mutexa);
		V(fullb);
	}
}
 
B(){
	while(1){
		P(fullb);
		P(mutexb);
		从A的信箱取出一个邮件;
		V(mutexb);
		V(emptyb);
		回答问题并提出一个新问题;
		P(emptya);
		P(mutexb);
		将新邮件放入B的邮箱;
		V(mutexb);
		V(fulla);
	}
}

2017 年真题

某进程中有 3 个并发执行的线程 thread 1, thread 2 和 thread 3。

//复数的结构类型定义
typedef struct{
	float a;
	float b;
}cnum;
 
cnum x,y,z;//全局变量
 
//计算两个复数之和
cnum add(cnum p,cnum q){
	cnum s;
	s.a = p.a+q.a;
	s.b = p.b+q.b;
	return s;
}
semaphore mutex_y1 = 1;//线程1和3对y的互斥
semaphore mutex_y2 = 1;//线程2和3对y的互斥
semaphore mutex_z = 1;//线程2和3对z的互斥
 
thread1(){
	cnum w;
	P(mutex_y1);
	w = add(x,y);
	V(mutex_y1);
	...
}
 
thread2(){
	cnum w;
	P(mutex_y2);
	P(mutex_z);
	w = add(y,z);
	P(mutex_z);
	P(mutex_y2);
}
 
thread3(){
	cnum w;
	w.a = 1;
	w.b = 1;
	P(mutex_z);
	z = add(z,w);
	V(mutex_z);
	
	P(mutex_y1);
	P(mutex_y2);
	y = add(y,w);
	V(mutex_y2);
	V(mutex_y1);
 
}

2020 年真题

现有 5 个操作 A、B、C、D 和 E,操作 C 必须在 A 和 B 完成后执行,操作 E 必须在 C 和 D 完成后执行。

semaphore a = 0,b = 0,c = 0,d = 0;
A(){
	...;
	V(a);
}
 
B(){
	...;
	V(b);	
}
 
C(){
	P(a);
	P(b);
	...;
	V(c);
}
 
D(){
	...;
	V(d);
}
 
E(){
	P(c);
	P(d);
	...;
}

2022 年真题

某进程的两个线程 T 1 和 T 2 并发执行 A、B、C、D、E 和 F 共 6 个操作,其中 T 1 执行 A、E 和 F,T 2 执行 B、C 和 D。两个操作的执行顺序所满足的约束:C 在 A 和 B 完成后执行,D 和 E 在 C 完成后执行,F 在 E 完成后执行。

semaphore a = b = c = d = e = f = 0;
 
T1(){
	A;
	Signal(a);
	Wait(c);
	E;
	F;
}
 
T2(){
	B;
	Wait(a);
	C;
	Signal(c);
	D;
}