前言
解决同步互斥问题的思路,源于对王道讲解的总结笔记
同类型题目:
【考研】操作系统:2009年真题45(同步互斥问题)_住在阳光的心里的博客-CSDN博客
【考研】操作系统:2014年真题47(同步互斥问题)_住在阳光的心里的博客-CSDN博客
【考研】操作系统:2019年真题43(同步互斥问题)_住在阳光的心里的博客-CSDN博客
一、思路
解决同步互斥问题,思路步骤:
1. 分析各进程之间的同步互斥关系;
2. 设置互斥信号量 mutex = 1、同步信号量(一般设可使用的资源数为empty = N)。
3. 最好画出同步互斥关系图,并判断属于哪一种类型,如生产者-消费者问题、读者-写者问题、哲学家进餐问题、吸烟者问题。
二、题目
45. 有 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)操作,以实现上述过程的同步。 要求写出完整过程,并说明信号量的含义和初值。
解:
(1)互斥资源:进程 A、B 对两个信箱的访问都是互斥的。有两个信箱,所以设置两个互斥信号量。mutex_A 和 mutex_B 分别用于对 A、B 信箱的互斥访问。
(2)同步问题:
A 信箱中有信时 A 才能取信,B 信箱中有信时 B 才能取信,设同步信号量 Full_A 、Full_B;
B 信箱中有空位时 A 才能放信,A 信箱中有空位时 B 才能放信,设同步信号量 empty_A、empty_B;
由于初始时 A 的信箱中有 x 个邮件(0 < x < M),B 的信箱中有 y 个(0 < y < N)
所以 Full_A = x、Full_B = y、empty_A = M - x、empty_B = N - y;
(3)A、B之间的同步互斥关系图如下:
(4) 伪代码如下:
semaphore mutex_A = 1; // mutex_A 用于 A 的信箱互斥
semaphore mutex_B = 1; // mutex_B 用于 B 的信箱互斥
semaphore Empty_A = M - x; // empty_A 表示 A 信箱中还可存放的邮件数量
semaphore Full_A = x; // Full_A 表示 A 信箱中的邮件数
semaphore Empty_B = N - y; // empty_B 表示 B 信箱中还可存放的邮件数量
semaphore Full_B = x; // Full_B 表示 B 信箱中的邮件数
分解步骤:一是先整互斥关系:
A(){ // 先使 A 和 B 互斥访问信箱
while(TRUE){
p(mutex_A);
从A的信箱中取出一个邮件;
v(mutex_A);
回答问题竟提出新问题;
p(mutex_B);
将邮件放入B的信箱中;
v(mutex_B);
}
}
B() {
while(TRUE){
p(mutex_B);
从B的信箱中取出一个邮件;
v(mutex_B);
回答问题竟提出新问题;
p(mutex_A);
将邮件放入A的信箱中;
v(mutex_A);
}
}
二是整同步关系:(分为两步)
第一步:从 A 取和往 A 放,从 B 取和往 B 放。
A(){ // 先使 A 和 B 互斥访问信箱
while(TRUE){
P(Full_A); // 起初信箱 A 有 x 封邮件,对 A 取邮件,则改变 x 值
p(mutex_A)
从A的信箱中取出一个邮件;
v(mutex_A)
回答问题竟提出新问题;
p(mutex_B)
将邮件放入B的信箱中;
v(mutex_B)
V(Full_B); // 往 B 信箱放邮件,则改变 y 值
}
}
B() {
while(TRUE){
P(Full_B); // 起初信箱 B 有 y 封邮件,对 B 取邮件,则改变 y 值
p(mutex_B)
从B的信箱中取出一个邮件;
v(mutex_B)
回答问题竟提出新问题;
p(mutex_A)
将邮件放入A的信箱中;
v(mutex_A)
V(Full_A); // 往 A 信箱放邮件,则改变 x 值
}
}
第二步:判断此时 A、B 信箱内是否还可存放邮件
A(){ // 先使 A 和 B 互斥访问信箱
while(TRUE){
P(Full_A); // 起初信箱 A 有 x 封邮件,对 A 取邮件,则改变 x 值
p(mutex_A);
从A的信箱中取出一个邮件;
v(mutex_A);
V(Empty_A); // 从 A 中取出一个邮件后,A 的容量加一
回答问题竟提出新问题;
P(Empty_B); // 判断此时 B 信箱内是否还可存放邮件
p(mutex_B);
将邮件放入B的信箱中;
v(mutex_B);
V(Full_B); // 往 B 信箱放邮件,则改变 y 值
}
}
B() {
while(TRUE){
P(Full_B); // 起初信箱 B 有 y 封邮件,对 B 取邮件,则改变 y 值
p(mutex_B);
从B的信箱中取出一个邮件;
v(mutex_B);
V(Empty_B); // 从 B 中取出一个邮件后,B 的容量加一
回答问题竟提出新问题;
P(Empty_A); // 判断此时 A 信箱内是否还可存放邮件
p(mutex_A);
将邮件放入A的信箱中;
v(mutex_A);
V(Full_A); // 往 A 信箱放邮件,则改变 x 值
}
}
完整代码如下:
semaphore mutex_A = 1; // mutex_A 用于 A 的信箱互斥
semaphore mutex_B = 1; // mutex_B 用于 B 的信箱互斥
semaphore Empty_A = M - x; // empty_A 表示 A 信箱中还可存放的邮件数量
semaphore Full_A = x; // Full_A 表示 A 信箱中的邮件数
semaphore Empty_B = N - y; // empty_B 表示 B 信箱中还可存放的邮件数量
semaphore Full_B = x; // Full_B 表示 B 信箱中的邮件数
A(){
while(TRUE){
p(Full_A);
p(mutex_A);
从A的信箱中取出一个邮件;
v(mutex_A);
v(Empty_A);
回答问题竟提出新问题;
p(Empty_B);
p(mutex_B);
将邮件放入B的信箱中;
v(mutex_B);
v(Full_B);
}
}
B() {
while(TRUE){
p(Full_B);
p(mutex_B);
从B的信箱中取出一个邮件;
v(mutex_B);
v(Empty_B);
回答问题竟提出新问题;
p(Empty_A);
p(mutex_A);
将邮件放入A的信箱中;
v(mutex_A);
v(Full_A);
}
}
另外一种解法(由评论区粉丝提供的思路编写而成)
semaphore numA = x; //初始A信箱中信的数量
semaphore numB = y; //初始B信箱中信的数量
semaphore mutexA = 1; //实现对A的互斥访问
semaphore mutexB = 1; //实现对B的互斥访问
A(){
while(true){
p(mutexA);
if(numA > 0){
numA--;
从A中取件;
}
V(mutexA);
回答并提问;
P(mutexB);
if(numB < N){
numB++;
放入B中;
}
V(mutexB);
}
}
B(){
while(true){
p(mutexB);
if(numB>0){
numB--;
从B中取件;
}
V(mutexB);
回答并提问;
P(mutexA);
if(numA < M){
numA++;
放入A中;
}
V(mutexA);
}
}