说明:问题摘录于王道考研书籍,作为学习记录使用,其中一些经典的进程同步互斥问题,写了一下源代码测试跑了一下。
详见:
进程同步与互斥——哲学家就餐问题源码实现(dining philosopher’s problem)
进程同步与互斥——读者/写者问题源码实现(reader-writer lock)
进程同步与互斥——吸烟者问题源码实现(cigarette smoker’s problem)
进程同步与互斥——理发师问题源码实现(sleeping barber problem)
下面index索引的题目实现均用伪代码所实现
进程同步互斥问题汇总
index
生产者——消费者问题
semaphore empty = n; //空闲缓冲区
semaphore full = 0; //缓冲区初始化为空
semaphore mutex = 1; //临界区互斥信号量
producer() {
while(1) {
produce an item;
p(empty);
p(mutex);
add an item to buffer;
v(mutex);
v(full);
}
}
consumer() {
while(1) {
p(full);
p(mutex);
remover an item from buffer;
v(mutex);
v(empty);
consume the item;
}
}
index1
问题描述:
三个进程P1,P2,P3互斥使用一个包含N(N>0)个单元的缓冲区。
P1每次用 produce()生成一个正整数并用put()送入缓冲区某一空单元。
P2每次用 getodd()从该缓冲区中取出一个奇数并用countodd()统计奇数个数。
P3每次用 geteven()从该缓冲区中取出一个偶数并用counteven()统计偶数个数。
请用信号量机制实现这三个进程的同步与互斥活动,并说明所定义的信号量的含义(要求用伪代码描述)。
问题分析:
semaphore empty = N; //缓冲区的大小N
//semaphore full = 0; //no use
semaphore odd = 0; //取奇数进程的consumer和producer之间的同步信号量
semaphore even = 0; //取偶数进程的consumer和producer之间的同步信号量
semaphore mutex = 1;
process producer()
{
int number = produce();
P(empty);
P(mutex);
//put(number)
V(mutex);
if(number % 2 == 0)
{
V(even);
}
else
{
V(odd);
}
}
process consumer1()
{
P(odd);
P(mutex);
//getodd()
V(mutex);
V(empty);
//countodd();
}
process consumer2()
{
P(even);
P(mutex);
//geteven()
V(mutex);
V(empty);
//counteven();
}
index2
问题描述:
某博物馆最多可容纳500人同时参观,有一个出入口,该出入口一次仅允许一个人通过。参观者的活动描述如下:
cobegin
参观者进程i:
{
//进门
//参观
//出门
}
coend
请添加必要的信号量和P、V(或wait()、signal())操作,以实现上述过程中的互斥与同步。要求写出完整的过程,说明信号量的含义并赋初值。
问题分析:
//错误的解法,该题中提到了只有一个出入口,就是只有一个门,可以是出,也可以入,每次只能一个人过
semaphore out = 1;
semaphore in = 1;
semaphore buffer = 500;
cobegin
参观者进程i:
{
P(buffer);
P(in);
//进门
V(in);
//参观
P(out);
//出门
V(out);
V(buffer);
}
coend
出入口一次仅允许一个人通过,设置互斥信号量mutex,初值为1。博物馆同时最多可容纳500人,因此设置信号量empty,初值为500。
semaphore mutex = 1;
semaphore empty = 500;
cobegin
参观者进程i:
{
P(empty);
P(mutex);
//进门
V(mutex);
//参观
P(mutex);
//出门
V(mutex);
V(empty);
}
coend
index3
问题描述:
某工厂有两个生产车间和一个装配车间,两个生产车间分别生产A、B两种零件,装配车间的任务是把A、B两种零件组装成产品。两个生产车间每生产一个零件后都要分别把它们送到装配车间的货架F1、F2上,F1存放零件A,F2存放零件B,F1和F2的容量均为可以存放10个零件。装配工人每次从货架上取一个A零件和一个B零件然后组装成产品。请用多线程并发进行正确的管理。
问题分析:
这个问题是本质还是生产者,消费者模型,其中生产者A车间和消费者车间共享缓冲区F1,生产者B车间和消费者车间共享缓冲区F2。因为,对于每个车间而言,其访问缓冲区都是并发进行的,可能有多个车间工人同时对缓冲区的状态进行改变,因此这个模型是多生产者/多消费者模型,在进程间代码中对于缓冲区状态改变的部分都需要进行互斥操作,以防止同时改变缓冲区状态而出错。
semaphore empty1 = 10; //F1的空闲容量
semaphore full1 = 0; //F1中A零件数目
semaphore mutex1 = 1; //用于进程间互斥访问缓冲区
semaphore empty2 = 10;
semaphore full2 = 0;
semaphore mutex2 = 1;
process1()
{
while(1)
{
//生产零件A
P(empty1); //判断货架F1是否为空
P(mutex1); //互斥访问货架F1
//将产品A存放在货架F1上
V(mutex1); //释放货架F1
V(full1); //货架F1上的零件A的个数加1
}
}
process2()
{
while(1)
{
//生产零件B
P(empty2); //判断货架F2是否为空
P(mutex2); //互斥访问货架F2
//将产品B存放在货架F2上
V(mutex2); //释放货架F2
V(full2); //货架F2上的零件B的个数加1
}
}
process3()
{
while(1)
{
P(full1); //F1货架上是否有零件
P(mutex1); //互斥访问货架F1
//在F1货架上取A零件
V(mutex1); //释放货架F1
V(empty1); //F1货架的空位数加1
P(full2);
P(mutex2);
//在F2货架上取B零件
V(mutex2);
V(empty2);
//将A零件和B零件组装成产品
}
}
index4
问题描述:
某寺庙,有小和尚、老和尚若干。有一水缸,由小和尚用水桶从井中提水入缸,老和尚用水桶从缸里取水饮用。水缸可容10桶水,水取自同一井中。水井径窄,每次只能容一个水桶取水。水桶总数为3个。每次入、取缸水仅为1桶,且不可以同时进行。试用P、V操作给出小和尚、老和尚动作的算法描述。
问题分析:
首先对于水井和水缸都是临界资源,每次只能由一个进程去访问,故分别设置mutex1和mutex2用于互斥访问水井和水缸。
另外,水缸作为缓冲区,可以放置10桶水,用于同步老和尚进程取水和小和尚进程放水的动作,经典的生产者——消费者(有界缓冲区问题),故设置,g_empty = 10, g_full = 0。
最后,无论对于老和尚进程来说,还是对小和尚进程来说,每次取水放水前,都需要拿到桶资源,桶资源是固定的为3个桶,这里的桶资源是不需要两个进程同步操作的,故设置,t_empty = 3。
semaphore t_empty = 3;
semaphore g_empty = 10;
semaphore g_full = 0;
semaphore mutex1 = 1;
semaphore mutex2 = 1;
process1()
{
P(g_empty); //看水缸有没有装满
P(t_empty); //看还有没有桶
P(mutex1); //互斥访问水井
//从水井中取一桶水
V(mutex1); //离开
P(mutex2); //互斥访问水缸
//将桶里的水倒进水缸中
V(mutex2); //离开
V(t_empty); //空闲桶数+1
V(g_full); //水缸中的水多出一桶
}
process2()
{
P(g_full);
P(t_empty);
P(mutex2);
//从缸中取水
V(mutex2);
V(t_empty);
V(g_empty);
}
index5
问题描述:
如下图所示,三个合作进程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的值打印出来。请用信号量实现它们的同步。
问题分析:
首先,三个进程通过一个设备获取数据,并且题目要求设备必须互斥地使用,而且第一个数据必须由P1进程读取,第二个数据必须由P2进程读取,第三个数据必须由P3进程读取。所以P1、P2、P3之间应该设置三个信号量来同步三个进程的输入。
使用上述信号量后,三个进程不会同时使用输入设备,因此不必再为输入设备设置互斥信号量。
另外,P1计算结果x,需要P2提供b,设信号量Sb用于同步。
P3计算需要P2提供y,设置信号量Sy用于同步。
P1打印需要P2提供y,需要P3提供z,设置信号量Sz用于同步。
semaphore S1 = 1; //初始状态下,P1进程第一个可以直接从设备读取数据,因此s1的值为1
semaphore S2 = 0;
semaphore S3 = 0;
semaphore Sb = 0;
semaphore Sy = 0;
semaphore 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);
V(Sy);
}
P3()
{
P(S3);
//输入c
P(Sy);
//z = y + c - a
V(Sz);
}
index6
问题描述:
某银行提供1个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空座位,则到取号机上领取一个号,等待叫号。取号机每次仅允许一位顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务。顾客和营业员的活动过程描述如下:
cobegin
{
process 顾客i
{
//从取号机获得一个号码;
//等待叫号;
//获得服务;
}
process 营业员
{
while (TRUE)
{
//叫号;
//为顾客服务;
}
}
}
coend
请添加必要的信号量和P、V(或wait()、signal())操作,实现上述过程中的互斥与同步。要求写出完整的过程,说明信号量的含义并赋初值。
问题分析:
互斥资源:取号机,设置互斥信号量mutex。
同步问题:顾客需要获得空座位等待叫号。营业员空闲时,将选取一位顾客并为其服务。空座位的有和无影响等待顾客的数量,顾客的有无决定了营业员是否能开始服务,因此分别设置信号量empty和full来实现这一同步关系。
另一个同步问题是,顾客获取座位后,需要等待叫号和被服务,这样,顾客与营业员就服务何时开始又构成了一个同步关系,定义信号量service来完成这一同步过程。
semaphore mutex = 1; //用于互斥访问取号机
semaphore empty = 10; //10个座位资源
semaphore full = 0; //多少个座位已经被占用了
semaphore service = 0; //顾客等待营业员叫号
process Pi
{
P(empty); //顾客来了,并且有空座位
P(mutex); //互斥访问取号机资源
//从取号机获取一个号码
V(mutex);
V(full); //被顾客占用的座位数+1
//等待叫号
P(service);
//获取服务
}
process s
{
while(true)
{
P(full); //营业员发现座位上有顾客等待
//叫号
V(service); //为该号码的顾客提供服务
V(empty); //顾客离开座位,空座位数+1
//为顾客服务
}
}
index7
问题描述:
有桥如下图所示。车流方向如箭头所示。回答如下问题:
1)假设该桥上每次只能有一辆车行驶,试用信号灯的PV操作实现交通管理。
2)假设桥上不允许两车交汇,但允许同方向多个车一次通过(即桥上可有多个同方向行驶的车)。试用信号灯的P、V操作实现桥上交通管理。
问题分析:
第一问比较简单,直接用一个mutex就可以解决。
第二问,允许同方向的多辆车一次通过桥,不允许不同方向的车同时过桥,再加一个互斥量进行控制即可。
semaphore mutex = 1; //桥的互斥访问
semaphore mutexSN = 1; //countSN为临界资源,每次访问时,需要加锁
semaphore mutexNS = 1; //countNS为临界资源,每次访问时,需要加锁
int countSN = 0; //从南到北车量的数目
int countNS = 0; //从北到南车量的数目
process NtoS
{
P(mutexNS);
countNS++;
if(countNS == 1)
P(mutex);
V(mutexNS);
//过桥
P(mutexNS);
countNS--;
if(countNS == 0)
V(mutex);
V(mutexNS);
}
process StoN
{
P(mutexSN);
countSN++;
if(countSN == 1)
P(mutex);
V(mutexSN);
//过桥
P(mutexSN);
countSN--;
if(countSN == 0)
V(mutex);
V(mutexSN);
}
index8
问题描述:
设自行车生产线上有一个箱子,其中有N个位置(N >= 3),每个位置可存放一个车架或一个车轮;又设有3名工人,其活动分别是:
工人1活动
do{
//加工一个车架;
//车架放入箱中;
}while(1)
工人2活动
do{
//加工一个车轮
//车轮放入箱中
}while(1)
工人3活动
do{
//箱中取出一个车架;
//箱中取出二个车轮
//组装为一台车
}while(1)
试分别用信号量与PV操作实现三名工人的合作,要求解中不含死锁。
问题分析:
首先不考虑死锁问题,工人1与工人3、工人2与工人3,构成生产者与消费者关系,这两对生产者\消费者关系通过共同的缓冲区相联系。从资源角度来看,箱子中的空位置相当于工人1和工人2的资源,而车架和车轮相当于工人3的资源。
分析上述解法易见:
当工人1推进速度较快时,箱中空位置可能完全被车架占满或只留一个存放车轮的位置,此时工人3同时取2个车轮将无法得到,而工人2又无法将新加工的车轮放入箱子。
当工人2推进速度较快时,箱中空位置可能完全被车轮占满,而此时工人3取车架将无法得到,而工人1又无法将新加工的车架放入箱中。
上述两种情况都意味着死锁,为防止死锁的发生,箱中车架的数量不可超过N-2,车轮的数目不可超过N-1,这些限制可以用两个信号量来表达。
semaphore empty = N; //空位置
semaphore wheel = 0; //车架
semaphore frame = 0; //车轮
semaphore limit_wheel = N - 1; //车轮最大数
semaphore limit_frame = N - 2; //车架最大数
process worker1:
do{
//加工一个车架
P(limit_frame); //检查车架数是否达到最大值
P(empty); //检查是否有空位
//将车架放入箱中
V(frame); //车架数加1
}while(1)
process worker2:
do{
//加工一个车轮
P(limit_wheel); //检查车轮数是否达到最大值
P(empty); //检查是否有空位
//将车轮放入箱中
V(frame); //车轮数加1
1
process worker3:
do{
P(frame); //检查是否有车架
//箱中取出一个车架
V(empty); //空位数加1
V(limit_frame); //可装入车架数加1
P(wheel); //检查是否有一个车轮
P(wheel); //检查是否有另一个车轮
//箱中取出两个车轮
V(empty); //取走一个车轮,空位数加1
V(empty); //取走另一个车轮,空位数加1
V(limit_wheel); //可装入车轮数加1
V(limit_wheel); //可装入车轮数再加1
//组装成一台车
}while(1)
index9
问题描述:
设P、Q、R共享一个缓冲区,P、Q构成一对生产者——消费者,R即是生产者又是消费者。使用P、V操作实现同步。
问题分析:
P,Q构成消费者——生产者关系,因此设置三个信号量full、empty、mutex。full和empty用来控制缓冲区的状态,mutex用来实现互斥进入。R即为消费者又为生产者,因此必须在执行前判断状态,若empty == 1,则执行生产者功能,若是full == 1,执行消费者功能。
semaphore empty = 1;
semaphore full = 0;
semaphore mutex = 1;
process P()
{
while(1)
{
P(empty);
P(mutex);
//向缓冲区中放入产品
V(mutex);
V(full);
}
}
process Q()
{
while(1)
{
P(full);
V(mutex);
//从缓冲区中取出产品
V(mutex);
V(empty);
}
}
process R()
{
if(empty == 1)
{
P(empty);
P(mutex);
//向缓冲区中放入产品
V(mutex);
V(full);
}
if(full == 1)
{
P(full)
V(mutex);
//从缓冲区中取出产品
V(mutex);
V(empty);
}
}
index10
问题描述:
假设一个录像厅有1,2,3三种不同的录像片可由观众选择放映,录像厅的放映规则如下:
1、任一时刻最多只能放映一一种录像片,正在放映的录像片是自动循环放映的,最后一名观众主动离开时结束当前录像片的放映。
2、选择当前正在放映的录像片的观众可立即进入,允许同时有多位选择同一种录像片的观众同时观看,同时观看的观众数量不受限制。
3、等待观看其他录像片的观众按到达顺序排队,当一种新的录像片开始放映时,所有等待观看该录像片的观众可依次序进入录像厅同时观看。用一个进程代表一个观众,要求:用信号量方法PV操作实现,并给出信号量定义和初始值。
问题分析:
该题的临界资源是放映厅,三组观众以互斥的方式轮流占用,可以看做三组进程竞争资源。
定义三个计数器count,统计观看录像片的观众个数,当每组第一个观众到来时,要负责竞争临界资源,最后一个离开时要释放资源。
信号量mutex用来控制三组观众互斥使用放映厅;信号量mutex1、mutex2、mutex3控制同组观众对count的访问。
semaphore mutex1 = 1;
semaphore mutex2 = 1;
semaphore mutex3 = 1;
semaphore mutex = 1;
int count1 = 0;
int count2 = 0;
int count3 = 0;
process audience_movie1()
{
P(mutex1);
count1++;
if(count1 == 1)
{
P(mutex);
}
V(mutex1);
// see movie1
P(mutex1);
count1--;
if(count1 == 0)
{
V(mutex);
}
V(mutex1);
}
process audience_movie2()
{
P(mutex2);
count2++;
if(count2 == 1)
{
P(mutex);
}
V(mutex2);
// see movie2
P(mutex2);
count2--;
if(count2 == 0)
{
V(mutex); //第一部影片没有人看了,放映结束
}
V(mutex2);
}
process audience_movie3()
{
P(mutex3);
count3++;
if(count3 == 1)
{
P(mutex);
}
V(mutex3);
// see movie3
P(mutex3);
count3--;
if(count3 == 0)
{
V(mutex);
}
V(mutex3);
}
index11
问题描述:
在南开大学至天津大学间有一条弯曲的路,每次只允许一辆自行车通过,但中间有小的安全岛M(同时允许两辆车),可供两辆车在已进入两端小车错车,如下图所示。设计算法并使用P、V操作实现。
问题分析:
由于安全岛M仅允许两辆车停留,本应作为临界资源而设置信号量,但根据题意,任意时刻进入安全岛的车不会超过两辆(两个方向最多各有一辆),因此不需要为M设置信号量。
需要在路口T和路口N都设置信号量,以控制来自两个方向的车对路口资源的争夺。这两个信号量的初值都是1。此外,由于从N到T的一段路只允许一辆车通过,所有还需要设置另外的信号量用于控制,由于M的存在,可为两端的小路分别设置一个互斥信号量。
semaphore T2N = 1;
semaphore N2T = 1;
semaphore K = 1;
semaphore L = 1;
process bike_T2N()
{
P(T2N);
P(L);
//from T to L;
//go into M;
V(L);
P(K);
//from K to N;
V(K);
V(T2N);
}
process bile_N2T()
{
P(N2T);
P(K);
//from N to K;
//go into M;
V(K);
P(L);
//from L to T;
V(L);
V(N2T);
}
index12
问题描述:
系统中有多个生产者进程和多个消费者进程,共享一个能存放 1000 件产品的环形缓冲区(初始为空)。当缓冲区未满时,生产者进程可以放入其生产的一件产品,否则等待;当缓冲区未空时,消费者进程可以从缓冲区取走一件产品,否则等待。要求一个消费者进程从缓冲区连续取出 10 件产品后,其他消费者进程才可以取产品。请使用信号量 P,V(wait(),signal())操作实现进程间的互斥与同步,要求写出完整的过程,并说明所用信号量的含义和初值。
问题分析:
这是典型的生产者——消费者问题,只对典型问题加了一个条件,只需在标准模型上新加一个信号量,即可完成。
semaphore empty = 1000; //缓冲区的空位数
semaphore full = 0; //缓冲区的产品数
semaphore mutex1 = 1; //控制一个消费者进程在一个周期内(10次)对缓冲区的访问
semaphore mutex2 = 1; //控制进程单次互斥地访问缓冲区
void* produce_process(void*)
{
while(1)
{
P(empty);
P(mutex2);
//向缓冲区放产品
V(mutex2);
V(empty);
}
}
void* consumer_process(void*)
{
while(1)
{
P(mutex1);
for(int i = 0; i < 10; i++)
{
P(full);
P(mutex2);
//从缓冲区中取产品
V(mutex2);
V(full);
}
V(mutex1);
}
}
index13
问题描述:
设公共汽车上,驾驶员和售票员的活动分别如下图所示:
驾驶员的活动:启动车辆,正常行车,到站停车;
售票员的活动:关车门,售票,开车门。
在汽车不断地到站、停车、行驶过程中,这两个活动有什么同步关系?用信号量和P、V操作实现它们的同步。
问题分析:
售票员关车门后,向驾驶员发开车信号,驾驶员接到开车信号后启动车辆,在汽车正常行驶过程中售票员售票,到站时驾驶员停车,售票员在车停后开门让乘客下车。
同步1:售票员关车门和驾驶员开车需要同步,设置信号量S1,表示是否允许驾驶员启动汽车(初值为0)
同步2:售票员开车门和驾驶员停车需要同步,设置信号量S2,表示是否允许售票员开车门(初值为0)
semaphore s1 = 0;
semaphore s2 = 0;
void* driver(void* arg)
{
P(s1);
//起动车辆
//正常行车
//到站停车
V(s2);
}
void* salewoman(void* arg)
{
//关车门
V(s1);
//售票
P(s2);
//开车门
}
index14
问题描述:
有A、B两人通过信箱进行辩论,每个人都从自己的信箱中取得对方的问题。将答案和向对方提出的新问题组成一个邮件放入对方的邮箱中。假设A的信箱最多放M个邮件,B的信箱最多放 N个邮件。初始时A的信箱中有x个邮件(0<x<M),B的信箱中有y个(0<y<N)。辩论者每取出一个邮件,邮件数减1。A和B两人的操作过程描述如下:
CoBegin()
A()
{
while(true)
{
从A的信箱中取出一个邮件
回答问题并提出一个新问题
将新邮件放入B的信箱
}
}
B()
{
while(true)
{
从B的信箱中取出一个邮件
回答问题并提出一个新问题
将新邮件放入A的信箱
}
}
CoEnd()
当信箱不为空时,辩论者才能从信箱中取邮件,否则等待。当信箱不满时,辩论者才能将新邮件放入信箱,否则等待。请添加必要的信号量和P、V(或wait、signal)操作,以实现上述过程的同步。要求写出完整过程,并说明信号量的含义和初值。
问题分析:
首先A、B可以看成两个进程,A的邮箱和B的邮箱可以看成两个缓冲区,A和B两个进程都可以对这两个缓冲区(临界资源)进行操作,所以两个缓冲区需要两把锁来保证同一时间只有一个进程可以对缓冲区进行操作。
另外,剩余的问题就是描述缓冲区的状态了,缓冲区不空的时候才可以取邮件,缓冲区不满的时候才可以放邮件。对于两个缓冲区分别需要设置empty和full两个条件变量。
semaphore a_empty = M - x; //a_empty,表示a的信箱中还可存放的邮件数量
semaphore a_full = x; //a_full,表示a的信箱中的邮件数量
semaphore b_empty = N - y; //b_empty,表示b的信箱中还可存放的邮件数量
semaphore b_full = y; //b_full,表示b的信箱中的邮件数量
semaphore a_mutex = 1; //a_mutex用于a信箱的互斥
semaphore b_mutex = 1; //b_mutex用于b信箱的互斥
void* A(void*)
{
while(1)
{
P(a_full);
P(a_mutex);
//从A的信箱中取出一个邮件
V(a_mutex);
V(a_empty);
//回答问题,并提出一个新的问题
P(b_empty);
P(b_mutex);
//将邮件放入B的信箱
V(b_mutex);
V(b_full);
}
}
void* B(void*)
{
while(1)
{
P(b_full);
P(b_mutex);
//从B的信箱中取出一个邮件
V(b_mutex);
V(b_empty)
//回答问题,并提出一个新的问题
P(a_empty);
P(a_mutex);
//将邮件放入A的信箱
V(a_mutex);
V(a_full);
}
}
index15
问题描述:
某进程中有3个并发执行的线程thread1、thread2和thread3,其伪代码如下所示。
请添加必要的信号量和P、V(或wait()、signal())操作,要求确保线程互斥访问临界资源,并且最大程度地并发执行。
问题分析:
对于三个全局变量x、y、z,访问x时,只有thread1访问,线程间没有互斥问题,访问y时,有三个线程访问,存在互斥的关系,访问z时,有两个线程访问,存在互斥的关系。
这个问题有些读者/写者问题的思想在里面,当thread1和thread2在读取y的数据时,thread3就不能对y的数据做任何更改(thread1和thread2可以同时读取数据)。当thread2读取z的数据时,同样的thread3也不能对z的数据做任何更改。
这样想,我们需要三个互斥量,thread1和thread3对于y变量的互斥访问,thread2和thread3对于y变量的互斥访问,thread2和thread3对于z变量的互斥访问。
semaphore mutex_y_13 = 1;
semaphore mutex_y_23 = 1;
semaphore mutex_z = 1;
thread1()
{
cnum w;
P(mutex_y_13);
w = add(x, y);
V(mutex_y_13);
}
thread2()
{
cnum w;
P(mutex_y_23);
P(mutex_z);
w = add(y, z);
V(mutex_z);
V(mutex_y_23);
}
thread3()
{
cnum w;
w.a = 1;
w.b = 1;
P(mutex_z);
z = add(z, w);
V(mutex_z);
P(mutex_y_13);
P(mutex_y_23);
y = add(y, w);
V(mutex_y_23);
V(mutex_y_13);
}
index16
问题描述:
有 n(n≥3)位哲学家围坐在一张圆桌边,每位哲学家交替地就餐和思考。在 圆桌中心有 m(m≥1)个碗,每两位哲学家之间有一根筷子。每位哲学家必须取到一个碗和两 侧的筷子后,才能就餐,进餐完毕,将碗和筷子放回原位,并继续思考。为使尽可能多的哲学 家同时就餐,且防止出现死锁现象,请使用信号量的 P、V 操作[wait()、signal()操作]描述上 述过程中的互斥与同步,并说明所用信号量及初值的含义。
问题分析:
该问题中,存在死锁的情况,当所有哲学家进餐的时候,都拿起左手边的筷子,这时他们每个都会阻塞住,并且一直等待另一个右手边的筷子。为了防止死锁的发生,选择一位哲学家用餐的顺序是先拿起右边筷子,再去获取左边筷子,至多允许4名哲学家同时进餐,这时循环等待的条件就会被打破,死锁问题就会解决。
semaphore chopstick[n];
semaphore bowl;
void init_semaphore()
{
for(int i = 0; i < n; i++)
{
chopstick[i] = 1;
}
bowl = m;
}
void philosopher_process_i(void* arg)
{
int i = *(int*)arg;
//第i个哲学家进程,i从0开始
if(i == 0)
{
P(chopstick[(i+1)%5]);
P(chopstick[i]);
}
else
{
P(chopstick[i]);
P(chopstick[(i+1)%5]);
}
P(bowl);
eat();
V(chopstick[i]);
V(chopstick[(i+1)%5]);
V(bowl);
think();
}
本题也可以用碗这个限制资源来避免死锁:当碗的数量m小于哲学家的数量n时,可以直接让碗的资源量等于m,确保不会出现所有哲学家都拿起一侧的筷子而无限等待另一侧的筷子进而造成死锁的情况。
当碗的数量m大于等于哲学家的数量n时,为了让碗起到同样的限制效果,我门让碗的资源量等于n-1,这样就能保证最多只有n-1名哲学家同时进餐,所以得到碗的资源量为min{n-1, m}。在进行PV操作时,碗的资源量起限制哲学家取筷子的作用,所以需要先对碗的资源量进行P操作。具体过程如下:
semaphore bowl; //用于协调哲学家对碗的使用
semaphore chopsticks[n]; //用于协调哲学家对筷子的使用
for(int i = 0; i < n; i++)
{
chopsticks[i] = 1; //设置两名哲学家之间筷子的数量
}
bowl = min(n-1, m);
philosopher_process_i()
{
think();
P(bowl);
P(chopsticks[i]);
P(chopsticks[(i+1)%5]);
//eat()
V(chopsticks[i]);
V(chopsticks[(i+1)%5]);
V(bowl);
}