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=0;
P1(){
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用于互斥,信号量为1。
int 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;
}
}