一个经典的同步问题:吸烟者问题( Patil ,1971年)。三名吸烟者在同一个房间内,还有一位香烟供应者。为了制造并抽掉香烟,每位吸烟者需要三样东西:烟草、纸和火柴,供应者有丰富的货物提供。三位吸烟者中,第一个人有自己的烟草,第二个人有自己的纸,第三个人有自己的火柴。供应者随机地将两样东西放在桌子上,允许一位吸烟者进行对健康不利的吸烟。当吸烟者完成吸烟后唤醒供应者,供应者再把两样东西放在桌子上,唤醒另一位吸烟者。试采用(1)信号量和 PV 操作、(2)管程,编写他们同步工作的程序。
时间: 2023-06-08 20:05:47 浏览: 385
对于这个问题,我可以为您提供两种实现方式:
1. 信号量和 PV 操作实现:
首先,我们需要定义三个信号量:tobacco, paper, match表示三个吸烟者是否拥有自己需要的物品;supply表示供应者当前有没有放置物品在桌子上;agent_signal表示当前是哪个吸烟者需要抽烟。
每个吸烟者和供应者分别是一个进程,代码如下:
// 初始化信号量
Semaphore tobacco(0);
Semaphore paper(0);
Semaphore match(0);
Semaphore supply(0);
Semaphore agent_signal(1); // 初始值为1表示让第一个吸烟者先抽烟
// 吸烟者1进程
void smoker1() {
while (true) {
tobacco.wait(); // 等待自己拥有烟草
supply.wait(); // 等待供应者放置物品
smoke(); // 吸烟
supply.signal(); // 唤醒供应者放置物品
}
}
// 吸烟者2进程
void smoker2() {
while (true) {
paper.wait(); // 等待自己拥有纸
supply.wait(); // 等待供应者放置物品
smoke(); // 吸烟
supply.signal(); // 唤醒供应者放置物品
}
}
// 吸烟者3进程
void smoker3() {
while (true) {
match.wait(); // 等待自己拥有火柴
supply.wait(); // 等待供应者放置物品
smoke(); // 吸烟
supply.signal(); // 唤醒供应者放置物品
}
}
// 供应者进程
void agent() {
while (true) {
agent_signal.wait(); // 等待当前需要抽烟的吸烟者
int items = rand() % 3; // 随机放置两个物品
switch (items) {
case 0: // 放置烟草和纸
tobacco.signal();
paper.signal();
break;
case 1: // 放置烟草和火柴
tobacco.signal();
match.signal();
break;
case 2: // 放置纸和火柴
paper.signal();
match.signal();
break;
}
supply.signal(); // 通知吸烟者可以抽烟了
}
}
// 吸烟函数
void smoke() {
// 这里可以加入一些表示吸烟的代码
agent_signal.signal(); // 通知供应者下一个需要抽烟的吸烟者
}
代码执行流程:首先供应者进程通过rand()函数随机生成两个物品,并通过semaphore类型的signal()函数唤醒两个需要这两个物品的吸烟者进程。一旦他们拥有了这些物品,就可以吸烟了。吸烟过程中,供应者进程等待吸烟者唤醒他,然后再生成下一组物品。这个过程一直循环。
2. 管程实现
这里我们用条件变量表示进程间需要等待某些条件满足的情况,以及互斥锁保证进程安全。
定义三个条件变量tobacco, paper, match和两个互斥锁table_mutex, smoke_mutex。tobacco代表烟草是否存在,paper代表纸是存在,match代表火柴是否存在;table_mutex用于保护在桌子上放置物品;smoke_mutex用于保证吸烟者不能同时抽烟。
每个吸烟者和供应者分别是一个进程,代码如下:
// 定义静态变量
static bool tobacco_exist = false;
static bool paper_exist = false;
static bool match_exist = false;
Condition tobacco;
Condition paper;
Condition match;
Mutex table_mutex;
Mutex smoke_mutex;
// 吸烟者1进程
void smoker1() {
while (true) {
smoke_mutex.lock(); // 加锁
while (!tobacco_exist) {
tobacco.wait(smoke_mutex); // 等待烟草存在
}
table_mutex.lock(); // 加锁
tobacco_exist = false; // 取走烟草
table_mutex.unlock(); // 解锁
smoke_mutex.unlock(); // 解锁
smoke(); // 吸烟
}
}
// 吸烟者2进程
void smoker2() {
while (true) {
smoke_mutex.lock();
while (!paper_exist) {
paper.wait(smoke_mutex);
}
table_mutex.lock();
paper_exist = false;
table_mutex.unlock();
smoke_mutex.unlock();
smoke();
}
}
// 吸烟者3进程
void smoker3() {
while (true) {
smoke_mutex.lock();
while (!match_exist) {
match.wait(smoke_mutex);
}
table_mutex.lock();
match_exist = false;
table_mutex.unlock();
smoke_mutex.unlock();
smoke();
}
}
// 供应者进程
void agent() {
while (true) {
table_mutex.lock();
int items = rand() % 3;
switch (items) {
case 0:
tobacco_exist = true;
paper_exist = true;
match_exist = false;
tobacco.signal();
paper.signal();
break;
case 1:
tobacco_exist = true;
paper_exist = false;
match_exist = true;
tobacco.signal();
match.signal();
break;
case 2:
tobacco_exist = false;
paper_exist = true;
match_exist = true;
paper.signal();
match.signal();
break;
}
table_mutex.unlock();
}
}
// 吸烟函数
void smoke() {
// 这里可以加入一些表示吸烟的代码
}
代码执行流程:供应者进程生成两个物品,并存储在变量中。在进程访问和修改变量时,加上了一个互斥锁table_mutex,确保各个进程的安全。然后通过条件变量tobacco、paper、match发送信号通知相应的吸烟者有需要的物品了。注意,吸烟者在等待物品的时候,需要将res_mutex加锁,等待到合适物品后释放res_mutex,抽完烟后再次获取res_mutex。同时,吸烟者在抽烟时,也需要将smoke_mutex加锁,确保各吸烟者不能同时抽烟。这个过程一直循环。