1. 问题描述
设自行车生产线上有一只箱子,其中有N个位置(N≥3),每个位置可存放一个车架或一个车轮,又设有三名工人,其活动分别为:
2. 问题分析(包括涉及的知识点、制约关系分析、问题的解决思路等)
根据题目意义分析,该题所述作业工人1、工人2与工人3构成生产者与消费者关系,存在着生产者-消费者问题的同步关系,涉及的知识点包括:信号量、PV原语、进程同步、死锁问题等。
问题解决思路:
用信号量与PV操作实现三个工人的合作,首先不考虑死锁问题,工人1与工人3、工人2与工人3构成生产者与消费者关系,这两对生产-消费关系通过共同的缓冲区——箱子相联系。从资源的角度来看,箱子中的空位置相当于工人1和工人2的资源,而车架和车轮相当于工人3的资源。定义三个信号量empty(空位置数量)、frame(车架数量)、wheel(车轮数量)来实现这种同步关系,分析会产生什么问题:
semaphore empty = N;//空位置数量
semaphore frame = 0;//车架数量
semaphore wheel = 0;//车轮数量
do{ //工人1活动
P(empty);
加工一个车架;
车架放入箱中;
V(frame);
}while(1)
do{ //工人2活动
P(empty);
加工一个车轮;
车轮放入箱中;
V(wheel);
}while(1)
do{ //工人3活动
P(frame);
P(wheel);
P(wheel);
箱中取一个车架;
箱中取一个车架;
组装一台车;
V(empty);
V(empty);
V(empty);
}while(1)
分析易见,当工人1加工速度较快时,箱中空位置可能完全被车架占满或只留有一个存放车轮的位置,而当此时工人3同时取2个车轮时将无法得到,而工人2又无法将新加工的车轮放入箱中;当工人2加工速度较快时,箱中空位置可能完全被车轮占满,而当此时工人3取车架时将无法得到,而工人1又无法将新加工的车架放入箱中。上述两种情况都意味着死锁的发生。为防止死锁的发生,箱中车架的数量不可超过N-2,车轮的数量不可超过N-1,这些限制可以添加另外两个信号量maxframe(箱中最大车架数)、maxwheel(箱中最大车轮数)来解死锁产生问题,解决方案如下。
3. 解决方案(包括所用工具介绍、方案流程图、方案伪代码等)
方案伪代码如下:
semaphore empty = N;//空位置数量
semaphore maxframe = N-2;//箱中最大车架数量
semaphore maxwheel = N-1;//箱中最大车轮数量
semaphore frame = 0;//车架数量
semaphore wheel = 0;//车轮数量
do{ //工人1活动
P(maxframe);
P(empty);
加工一个车架;
车架放入箱中;
V(frame);
}while(1)
do{ //工人2活动
P(maxwheel);
P(empty);
加工一个车轮;
车轮放入箱中;
V(wheel);
}while(1)
do{ //工人3活动
P(frame);
P(wheel);
P(wheel);
箱中取一个车架;
箱中取一个车架;
组装一台车;
V(maxframe);
V(maxwheel);
V(maxwheel);
V(empty);
V(empty);
V(empty);
}while(1)
工具介绍:
所用工具为jBACI,该工具包有自己的编译器和解释器,源代码有编译器编译,再解释执行,类似于java,该工具可以在windows10系统下直接运行。程序编写有“.cm”和“.pm”两种格式,“.cm”格式和C语言、C++相似,内置一些图形函数,具体可以参照docs文件夹下的说明书和examples文件夹下的范例来学习使用,使用前需要更改内部配置文件config.cfg文件,修改内容为:
SOURCE_DIRECTORY=.\examples\
PASCAL_COMPILER=.\bin\bapas.exe
C_COMPILER=.\bin\bacc.exe
(下载地址:https://github.com/motib/jbaci)
代码编写好后,需要点击Compile进行编译,才能够点击Run执行,进入执行页面可以看到有代码执行区、控制台、全局变量区等,可以对代码Add添加断点,可以step单步执行,点击Go执行后还可以很好看清各个进程的详细执行情况和各个变量的数值变化。
总之,jBACI是用来学习计算机操作系统进程同步与互斥制约关系的一个相当友好而且实用的工具。
方案示意流程图如下:
方案具体实现代码如下:
const int ValueOfCars = 50;//设定生产车的数量期望值
int sumframe = 0;//用于计数生产车架的数量
int sumwheel = 0;//用于计数生产车轮的数量
int sumcar = 0;//用于计数生产车的数量
semaphore empty = 6;//信号量:为了易于程序实现,设置位置N=6
semaphore maxframe = 4;//信号量:为了防止死锁产生,箱中最大可容纳的车架数
semaphore maxwheel = 5;//信号量:为了防止死锁产生,箱中最大可容纳的车轮数
semaphore frame = 0;//信号量:车架数初始值=0
semaphore wheel = 0;//信号量:车轮数初始值=0
void worker1() {
while (sumframe < ValueOfCars) {//为了防止死锁,给工人1一个停止条件
wait(maxframe);
wait(empty);
sumframe = sumframe + 1;
cout << "Worker1 is working " << endl;
signal(frame);
}
cout << "Worker1 have finished work " << endl;//工人1工作结束
}
void worker2() {
while (sumwheel < 2*ValueOfCars) {//为了防止死锁,给工人2一个停止条件
wait(maxwheel);
wait(empty);
sumwheel = sumwheel + 1;
cout << "Worker2 is working "<< endl;
signal(wheel);
}
cout << "Worker2 have finished work " << endl;//工人2工作结束
}
void worker3() {
while (sumcar < ValueOfCars) {//为了防止死锁,给工人3一个停止条件
wait(frame);
wait(wheel);
wait(wheel);
sumcar = sumcar + 1;
cout << "Worker3 is working "<< endl;
signal(maxframe);
signal(maxwheel);
signal(maxwheel);
signal(empty);
signal(empty);
signal(empty);
}
cout << "Worker3 have finished work " << endl;//工人3工作结束
}
void main() {
cobegin {
worker1();
worker2();
worker3();
}
cout << "Sumframe = " << sumframe << endl;//输出生产的车架总数
cout << "Sumwheel = " << sumwheel << endl;//输出生产的车轮总数
cout << "Sumcar = " << sumcar << endl;//输出生产的车总数
}
4.实验结果分析(包括结果展示、结果原因剖析等)
结果展示:
左边结果显示,工人1率先停下工作,工人2其次,工人3最后完成工作,右边结果显示,箱中车架数frame和车轮数wheel为0,箱中空位置数empty复位为6,最大可容纳车架数maxframe和最大可容纳车轮数maxwheel复位,生产出的车架数、车轮数、车辆数正好达到所设期望值,不超出也没减少,而且程序执行过程中并没有发出“A deadlock occured”的死锁警告。
结果原因剖析:
该结果符合预期,成功避免了死锁发生。原因一是新添了两个信号量最大可容纳车架数maxframe和最大可容纳车轮数maxwheel,阻止了生产中出现的工人1或2加工过快导致箱中无法凑齐一辆车必要部件的死锁情况,原因二是通过设置生产车量的期望值来给每个工人一个工作结束条件,避免了工人1或2其中一个率先结束工作时工人3未结束工作但因一个部件缺少而一直等待,另一工人也等待工人3拿走箱中部件才能继续加工的死锁情况。