1同步与互斥

同步就是先V后P,互斥就是先P后V。
三个进程P1,P2,P3互斥使用一个包含N(N>0)个单元的缓冲区。P1每次使用produce()生成一个正整数并用put()送入缓冲区某一空单元;P2每次用getodd()从该缓冲区中取出一个奇数并用countodd()统计奇数个数;P3每次用geteven()从该缓冲区中取出一个偶数并用conuteven()统计偶数个数。请用信号量机制实现这3个进程得同步与互斥活动,并说明所定义的信号量的含义。
首先本题中有两个缓冲区,第二个缓冲区是P1,P2,P3互斥访问的,因此信号量初值只能为1。
此外进程P1,P2,P3还需要实现同步,就是说P1先生成数并放入缓冲区,P2或P3才能从缓冲区中取数,因为同步是先V后P,因此信号量初值设置为0。
综合上述,标准答案是这样的
做这种题有几个要点,第一是信号量的设置要正确,是同步信号量还是互斥信号量要弄明白,二是题目中的函数要出现在代码中。

semaphore mutex = 1;//缓冲区操作互斥信号量
semaphore odd = 0,even = 0;//奇数,偶数进程的同步信号量
semaphore empty = N;//空缓冲区单元个数信号量
cobegin{
 Process P1()
 while(True)
 {
  x = produce();//生成一个数
  P(empty);
  P(mutex);
  put();
  V(mutex);//释放缓冲区
  if(x%2==0)
   V(even);//若是偶数,向P3发出信号
  else
   V(odd)}
 Process P2()
 while(True)
 {
  P(odd);
  P(mutex);
  getodd();
  V(mutex);
  V(empty);
  countodd();
 }
 Process P3()
 while(True)
 {
  P(even);
  P(mutex);
  geteven();
  V(mutex);
  V(empty);
  counteven();
 }
}coend

2 在一个仓库中可以存放A和B两种产品,要求:

(1)每次只能存入一种产品。
(2)A产品数量-B产品数量<M。
(3)B产品数量-A产品数量<N。
其实这道题给我整懵逼了,设互斥信号量mutex我会,但A和B的信号量怎么设置呢?
因为A和B都是变量,两个变量交互在一起,我感觉十分的头大,这时候就要利用极限的思想。如果仓库只存放A产品呢?如果仓库只存放B产品呢?
如果仓库只存放A产品,那么它的最大值就是M-1,同理,只存放B产品,最大值就是N-1。因为A和B之间有这种约束关系,所以它们应该是同步信号量。
在存入A产品的时候,首先进行P操作,看允不允许往仓库中放入A,放完A之后,基于A和B之间的约束关系,相比于之前,B可以多放一个,大概就是
这样的。

Semaphore Sa=M-1,Sb=N-1;//分别代表产品A和B还可容纳的数量差,以及B和A的数量差。
Semaphore mutex = 1;//访问仓库的互斥信号量
rocess_A()
{
 while(True){
  	P(Sa);
  	P(mutex);
  	A产品入库;
  	V(mutex);
  	V(Sb);
}
process_B()
{
 while(True){
  	P(Sb);
  	P(mutex);
  	B产品入库;
  	V(mutex);
  	V(Sa);
}

3面包师有很多面包,由n名销售人员推销。每名顾客进店后取一个号,并且等待叫号,当一名销售人员空闲后,就叫下一个号。试设计一个使销售人员和顾客同步的算法。

int i=0,j=0;
semaphore mutex_i=1;mutex_j=1;
Consumer(){
 进入面包店;
 P(mutex_i);
 取号i;
 i++;
 V(mutex_i);
 等待叫号i并购买面包;
}
Seller()
{
	while(True){
  		P(mutex_j);
  		if(j<=i){
  			叫号j;
   			j++V(mutex_j);
   			销售面包;
   		}
  		else{
  			V(mutex_j);
  			休息片刻;
  			}
  		}
}

4某博物馆最多可容纳500人同时参观,有一个出入口,该出入口一次仅允许一人通过。参观者的活动描述如下:

cobegin
	参观者进程i;
	{
		…………
		进门;
		…………
		参观;
		…………
		出门;
		……………
	}
coend

请添加必要的信号量和P,V[或wait(),aignal()]操作,以实现上述过程中的互斥与同步。
要求写出完整的过程,说明信号量的含义并赋初值。

出入口一次仅允许一个人通过,设置互斥信号量mutex,初值为1.博物馆最多可同时容纳500人,因此设置信号量empty,初值为500。

Semaphore empty = 500;//博物馆可以容纳的最多人数
Semaphore mutex = 1;//用于出入口资源的控制
cobegin
参观者进程i:
{
	……
	P(empty);//可容纳人数-1
	P(mutex);//互斥使用门
	进门;
	V(mutex);
	参观;
	P(mutex);//互斥使用门
	出门;
	V(mutex);
	V(empty);//可容纳人数+1
	……
}

5某工厂有两个生产车间和一个装配车间,两个生产车间分别生产A,B两种零件,装配车间的任务是把A,B两种零件组装成产品。两个车间每生产一个零件后,都要分别把它们送到专配车间的货架F1,F2上。F1存放零件A,F2存放零件B,F1和F2的容量均可存放10个零件。装配工人每次从货架上取一个零件A和一个零件B后组装成产品。用PV操作正确管理。

我发现做这种题,最重要的就是找准约束关系
(1)F1和F2的容量为10
(2)必须先放零件再取零件
这是我之前发现的约束关系,但我还忘了一个
生产车间要访问货架,装配工人也要访问货架,他俩总不能同时访问货架吧,他俩得互斥访问,我错就错在这个地方。

本题需要设置6个信号量:
empty1对应货架F1上的空闲空间,初值为10,empty2对应货架F2上的空闲空间,初值为10;
full1对应货架F1上的A产品,初值为0,full2对应货架F2上的B产品,初值为0;
mutex1用于互斥地访问货架F1,mutex2用于互斥地访问货架F2。
process_A
while(True){
	生产一个产品A;
	P(empty1);//判断货架F1是否有空
	P(mutex1);//互斥访问货架F1
	将产品A存放到货架F1上;
	V(mutex1);
	V(full1);
}
process_B
while(True){
	生产一个产品B;
 	P(empty2);//判断货架F2是否有空
 	P(mutex2);//互斥访问货架F2
 	将产品B存放到货架F2上;
 	V(mutex2);
 	V(full2);
 }
 assemble
 while(True){
 	P(full1);//判断货架F1上是否有产品A
 	P(mutex1);//互斥访问货架F1
 	从货架F1上取一个A产品;
 	V(mutex1);//释放货架F1
 	V(empty1);货架F1上的空闲空间数+1
 	P(full2);//判断货架F2上是否有产品B
	P(mutex2);//互斥访问货架F2
  	从货架F2上取一个B产品;
  	V(mutex2);//释放货架F2
  	V(empty2);货架F2上的空闲空间数+1
  	将取得的A产品和B产品组装成产品
}

6某寺庙有小和尚,老和尚若干,有一水缸,由老和尚提水入缸供老和尚饮用。水缸可容10桶水,水取自同一井中。水井径窄,每次只能容纳一个桶取水。水桶总数为3个。每次入缸取水仅为1桶水,且不可同时进行。试给出有关从缸取水,入水的算法描述。

在本题中我忽略了一个限制关系,那就是老和尚取水的时候,缸里没水怎么办?所以要多设置一个信号量表示缸中水的数量。


从井中取水并放入水缸是一个连续的动作,可视为一个进程;从缸中取水可视为另一个进程。设水井和水缸为临界资源,引入mutex1和mutex2;三个水桶无论是从井中取水还是将水倒入水缸都是一次一个,应该给他们一个信号量empty1,抢不到水桶的进程只好等待。水缸满时,不可以再放水,设置empty2信号量来控制入水量;水缸空时,不可以取水,设置empty3信号量来控制。
semaphore mutex1 = 1;//用于互斥地访问水井
semaphore mutex2 = 1;//用于互斥的访问水缸
semaphore empty2 = 10;//用于表示水缸中剩余空间能容纳的水的桶数
semaphore empty1 = 3;//表示有多少个水桶可用,初值为3
semaphore empty3 = 0;//表示水缸中水的桶数
//小和尚
while(True)
{
	P(mutex1);//互斥访问水井
	P(empty2);//申请使用一个水桶
	从井中打一桶水;
	V(mutex1);
	P(mutex2);//互斥访问水缸
	P(empty2);
	将水倒入水缸中;
	V(mutex2);
	V(empty1);
	V(empty3);
}
//老和尚
while(True)
{
	P(empty3);//申请从水缸中取水
	P(mutex2);//互斥访问水缸
	P(empty1);//申请使用一个水桶
	从水缸中打一桶水;
	V(mutex2);
	V(empty2);
	喝水;
	V(empty1);//释放一个水桶
}
这种题很需要加注释。

7如下图所示,三个合作进程P1,P2,P3,它们都需要通过同一设备输入各自的数据a,b,c,该输入设备必须互斥的使用,而且其第一个数据必须由P1进程读取,第二个数据必须由P2进程读取,第三个数据必须由p3进程读取。然后,三个进程分别对输入数据进行下列计算:


P1:x=a+b;
P2:y=a*b;
P3:z=y+c-a;
最后,P1进程通过所连接的打印机将计算结果x,y,z的值打印出来,请用信号量实现它们的同步。
这种题很少见,因为它居然没有mutex,就是只考了同步,我觉得真是太妙了。而且最后还要由进程P1把结果输出,这又是一个同步。另外这种有多个进程的还是要分开写。这题实在是太精妙了。

为了控制三个进程依次使用输入设备进行输入,需分别设置三个信号量S1,S2,S3,其中S1的初值为1,S2和S3的初值均为0.使用上述信号量后,三个进程不会同时使用输入设备,因此不必再为输入设备设置互斥信号量。另外,还需要设置信号量Sb,Sy,Sz来表示数据b是否已经输入,以及y,z是否已计算完成,他们的初值均为0。

semaphore S1=1,S2=0,S3=0;
semaphore Sb=0,Sy=0,Sz=0P1(){
	P(S1);
	从输入设备输入数据a;
	V(S2);
	P(Sb);
	x=a+b;
	P(Sy);
	P(Sz);
	使用打印机打印出x,y,z的结果;
}
P2(){
	P(S2);
	从输入设备输入数据b;
	V(S3);
	V(Sb);
	y=a+b;
	V(Sy);
}
P3(){
	P(S3);
	从输入设备输入数据c;
	P(Sy);
	z=y+c-a;
	V(Sz);
}

8

cobegin
semaphore seat = 10;//供顾客等待的座位,初始值为10
semaphore costomer = 0;//银行内在座位上等待的顾客数,初始为0
semaphore mutex = 1;//用于取号机的互斥访问
semaphore service =0;总归是营业员先叫号,然后顾客再离开座位去接受服务,这里又有一重同步关系,所以要设一个service。

{
	process顾客i;
	{	
		P(seat);
		P(mutex);
		从取号机获取一个号码;
		V(mutex);
		V(costomer);
		P(service);
		等待叫号;
		获取服务;
	}
	process 营业员
	{
		while(true)
		{
			P(costomer);//有顾客等待才叫号,没有顾客等待就不叫号			
			叫号;
			V(service);			
			为顾客服务;
			V(seat);//顾客接受服务完走了,空闲座位+1
		}
	}
}

9有桥如下图所示,车流方向如箭头所示,回答下列问题:

(1)假设桥上每次只能有一辆车行驶,试用信号灯的P,V操作实现交通管理。4
(2)假设桥上不允许两车交会,但允许同方向多辆车一次通过(即桥上可有多辆同方向行驶的车)。试用信号灯的P,V操作实现桥上的交通管理。


过桥错误答案

我说写着写着怎么一直感觉不对劲,原来是这里是要分成两个进程来写,分别是从北边往南走和从南边往北走。
semaphore bridge = 1;//用于互斥的访问桥
NtoS(){
	P(bridge);
	通过桥;
	V(brisge);
}
StoN(){
	P(bridge);
	通过桥;
	V(brisge);
}

(2)遇到这种没有给定具体数量的题目,得用count和if了
桥上可以同方向多车行驶,需要设置bridge,还需要对同方向车辆计数。为了防止同方向计数中同时申请bridge造成同方向不可同时行车的问题,要对计数过程加以保护,因为设置信号量mutexSN,mutexNS。

int countSN = 0;
int countNS = 0;
semaphore mutexSN = 1;
semaphore mutexNS = 1;
semaphore bridge = 1;
StoN(){
	P(mutexSN);
	if(countSN==0)
		P(bridge);
	countSN++;
	V(mutexSN);
	过桥;
	P(mutexSN);
	countSN--;
	if(countSN==0)
		V(bridge);
	V(mutexSN);
}
NtoS(){
	P(mutexNS);
	if(countNS==0)
		P(bridge);
	countNS++;
	V(mutexNS);
	过桥;
	P(mutexNS);
	countNS--;
	if(countNS==0);
		V(bridge);
	V(mutexNS);
}

10假设有两个线程(编号为0和1)需要去访问同一个共享资源,为避免竞争状态的问题,我们必须实现一种互斥机制,使得在任何时候只能有一个线程访问这个资源。假如有如下一段代码:

bool flag[2];//flag数组,初始化为FALSE
Enter_Critical_Section(int my_thread_id,int other_thread_id){
	while(flag[other_thread_id]==TRUE);
	flag[my_thread_id]=TRUE;
}
Exit_Critical_Section(int my_thread_id,int other_thread_id){
	flag[my_thread_id]=FALSE;
}

当一个线程想要访问临界资源时,就调用上述的这两个函数。例如,线程0的代码可能是这样的:
Enter_Critical_Section(0,1);
使用这个资源;
Exit_Critical_Section(0,1);
做其他的事情;
试问:
(1)上述的这种机制能够实现资源互斥访问吗?为什么?
(2)若把Enter_Critical_Section()函数中的两条语句互换一下位置,结果会如何?
一开始都是FALSE,线程1是TRUE的时候,线程0陷入忙等状态,直到线程1是FALSE的时候,才跳出死循环,去执行线程0,因此可以实现互斥访问。

啥玩意还要时钟中断,还要考虑原子性,我看408的题目咋没说原子性,我真是服气。好像真的考虑原子性,要不就是线程1执行完,线程0才能进行实现这种同步。

不能实现互斥访问。考虑如下情形:
(1)初始化时,flag数组的两个元素值均为FALSE。
(2)线程0先执行,执行while循环语句时,由于flag[1]为FALSE,所以顺利结束,不会被卡住。假设这时来了一个时钟中断,打断了它的运行。
(3)线程1去执行,执行while循环语句时,由于flag[0]为FALSE,所以顺利结束,不会被卡住,然后进入临界区。
(4)后来当线程0再执行时,也进入临界区,这样就同时有两个线程在临界区。
总结:不能成功的根本原因是无法保证Enter_Critical_Section函数执行的原子性。我们从上面的软件实现方法中可以看出,对于两个进程互斥,最主要的问题是标志的检查和修改不能作为一个整体来执行,因此容易导致无法保证互斥访问的问题。
如果两条语句换一下位置,那么无论线程1是什么状态,线程0都会区执行,这就无法实现互斥访问,会陷入死锁状态。

11同步与死锁

semaphore empty = N;
semaphore frame = 0;
semaphore wheel = 0;
semaphore s1 = N-2;
semaphore s2 = N-1;
工人1活动:
do{
	加工一个车架;
	P(s1);
	P(empty);
	车架放入箱中;
	V(frame);
}while(1);
工人2活动:
do{
	加工一个车轮;
	P(s2);
	P(empty);
	车轮放入箱中;
	V(wheel);
}while(1)
工人3活动:
do{
	P(frame);
	箱中取一车架;
	V(empty);
	V(s1);
	P(wheel);
	P(wheel);
	箱中取两个车轮;
	V(empty);
	V(empty);
	V(s2);
	V(s2);
	组装为一台车;
}while(1)


12设P,Q,R共享一个缓冲区,P,Q构成一对生产者-消费者,R既为生产者又为消费者。使用P,V操作使其同步。

其实本题的难点就在于如何理解这个R进程。R既为消费者又为生产者,因此必须在执行前判断状态,若empty=1,则执行生产者功能,若full=1,则执行消费者功能。

semaphore full = 0;//表示缓冲区的产品
semaphore empty = 1;//表示缓冲区的空位
semaphore mutex = 1;//互斥信号量
Procedure P
{
	while(True){
		P(empty);
		P(mutex);
		Product one;
		V(mutex);
		V(full);
		}
}
Procedure Q
{
	while(True){
		P(full);
		P(mutex);
		Consume one;
		V(mutex);
		V(empty);
		}
}
Procedure R
{
	if(empty==1){
		P(empty);
		P(mutex);
		produce one;
		V(mutex);
		V(full);
	}
	if(full==1){
		P(full);
		P(mutex);
		Consume one;
		V(mutex);
		V(empty);
	}
}

13理发店里有一位理发师,一把理发椅和n把供等候理发的顾客坐的椅子。若没有顾客,理发师便在理发椅上睡觉,一位顾客到来时,顾客必须叫醒理发师,若理发师正在理发时又有顾客到来,若有空椅子可坐,则坐下来等待,否则就离开。试用P,V操作实现,并说明信号量的定义和初值。

'''控制变量waiting用来记录等待理发的顾客数,初值为0,进来一个顾客时,waiting加1,一个顾客理发时,waiting减1。
信号量coustomers用来记录等候理发的顾客数,并用作阻塞理发师进程,初值为0。
信号量baebers用来记录正在等候顾客的理发师数,并用作阻塞理发师进程,初值为0。
信号量mutex用于互斥,信号量为1int waiting = 0;//等候理发的顾客数
int chairs = n;//为顾客准备的椅子数
semaphore cuotomers=0,barbers=0,mutex=1;
barber(){
	while(True){
		P(customers);//若无顾客,理发师睡眠
		P(mutex);//进程互斥
		waiting = waiting-1;//等候顾客数少1个
		V(barbers);//理发师为一个顾客理发
		V(mutex);
		Cut_hair();
		}
}
customer(){
	P(mutex);
	if(waiting<chairs){
		waiting=waiting+1;//等待顾客数加1
		V(customers);
		P(mutex);
		P(barbers);//无理发师,顾客坐着
		get_hair_cut();
		}
	else
		V(mutex);//人满,顾客离开
}





14 有录像厅和观众两个主体。

限制条件1:只有3种录像片
限制条件2:看其他录像片的观众不允许进入


这一题犯的错误是明明有三种想看电影的人,但我却把他们统一处理了。

16设公共汽车上驾驶员和售票员的活动分别如下图所示。驾驶员的活动:启动车辆,正常行驶,到站停车;售票员的活动:关车门,售票,开车门。在汽车不断地到站,停车,行驶的过程中,这两个活动有什么同步关系?用信号量和P,V操作实现它们的同步。


在汽车行驶过程中,驾驶员活动与售票员活动之间的同步关系为:售票员关闭车门后,向驾驶员发开车信号,驾驶员接到开车信号后启动车辆,在汽车正常行驶过程中售票员售票,到站时驾驶员离车,售票员在车停后开门让乘客上下车。因此,驾驶员启动车辆的动作必须和售票员关车门的动作同步;售票员开车门的动作也必须与驾驶员停车同步。应设置两个信号量S1,S2,S1表示是否允许驾驶员启动汽车,初值为0;S2表示是否允许售票员开门(初值为0)
semaphore S1=S2=0;
Procedure driver
{
	P(S1);
	Start;
	Driving;
	Stop;
	V(S2);
}
Procedure Conductor
{
	while(1)
	{
		关车门;
		V(S1);
		售票;
		P(S2);
		开车门;
		上下乘客;
	}
}

17信箱辩论


很nice,这种类型的题目我差不多已经掌握了。

18某进程中有3个并发执行的线程thread1,thread2和thread3,其伪代码如下所示:

//复数的结构类型定义
typedef struct
{	
	float a;
	float b;
} cnum;
cnum x,y,z;//全局变量

//计算两个复数之和
cunm add (cnump,cnumq)
{
	cnum s;
	s.a=p.a+q.a;
	s.b=p.b+q.b;
	return s;
}

}

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

20现有5个操作A,B,C,D和E,操作C必须在A和B完成后执行,操作E必须在C和D完成后执行,请使用信号量的wait(),signal()操作(P,V操作)描述上述操作之间的同步关系,并说明所用信号量及其初值。

相关文章