当前位置: 首页 > news >正文

【操作系统】吸烟者问题

问题描述

        吸烟者问题是一个经典的同步问题,涉及三个抽烟者进程和一个供应者进程。每个抽烟者需要三种材料(烟草、纸和胶水)来卷烟,但每个抽烟者只有一种材料。供应者每次提供两种材料,拥有剩下那种材料的抽烟者可以卷烟并抽掉它。

问题分析
  1. 同步关系:供应者与三个抽烟者之间存在同步关系,供应者提供材料后,抽烟者才能开始卷烟。

  2. 互斥关系:三个抽烟者之间是互斥的,即同一时间只能有一个抽烟者卷烟。

  3. 信号量设置:需要设置信号量来控制材料的供应和抽烟者的动作。

解决方案

使用信号量来实现同步和互斥:

  • 信号量定义

    • offer1offer2offer3:分别表示供应者提供的不同组合材料(如烟草和纸、烟草和胶水、纸和胶水)。

    • finish:用于通知供应者当前抽烟者已完成抽烟。

伪代码

  • 供应者

while(1){random = 任意一个整数随机数;random = random % 3;if (random == 0)V(offer1);  // 提供烟草和纸else if (random == 1)V(offer2);  // 提供烟草和胶水elseV(offer3);  // 提供纸和胶水P(finish);  // 等待抽烟者完成
}
  • 抽烟者

while(1){P(offerX);  // 等待供应者提供材料// 拿走材料,卷烟,抽掉V(finish);  // 通知供应者已完成
}
改进
  • 由于缓冲区大小为1,四个同步信号量中至多只有一个为1,因此finish信号量用于互斥是不必要的

实现详细代码: 

1. 供应者代码(supplier.c

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>#define NUM_SMOKERS 3// 信号量定义
sem_t offer1;  // 提供烟草和纸
sem_t offer2;  // 提供烟草和胶水
sem_t offer3;  // 提供纸和胶水
sem_t finish;  // 通知供应者当前抽烟者已完成void* supplier(void* arg) {while (1) {// 随机选择一组材料int random = rand() % NUM_SMOKERS;if (random == 0) {printf("Supplier: Offering tobacco and paper\n");sem_post(&offer1);  // 提供烟草和纸} else if (random == 1) {printf("Supplier: Offering tobacco and matches\n");sem_post(&offer2);  // 提供烟草和胶水} else {printf("Supplier: Offering paper and matches\n");sem_post(&offer3);  // 提供纸和胶水}// 等待抽烟者完成sem_wait(&finish);}
}int main() {pthread_t supplier_thread;// 初始化信号量sem_init(&offer1, 0, 0);sem_init(&offer2, 0, 0);sem_init(&offer3, 0, 0);sem_init(&finish, 0, 0);// 创建供应者线程pthread_create(&supplier_thread, NULL, supplier, NULL);// 主线程可以继续运行其他任务或等待pthread_join(supplier_thread, NULL);// 清理信号量sem_destroy(&offer1);sem_destroy(&offer2);sem_destroy(&offer3);sem_destroy(&finish);return 0;
}
2. 抽烟者代码(smoker.c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>#define NUM_SMOKERS 3// 信号量定义
extern sem_t offer1;  // 提供烟草和纸
extern sem_t offer2;  // 提供烟草和胶水
extern sem_t offer3;  // 提供纸和胶水
extern sem_t finish;  // 通知供应者当前抽烟者已完成void* smoker1(void* arg) {while (1) {sem_wait(&offer1);  // 等待供应者提供烟草和纸printf("Smoker 1: Making a cigarette with tobacco and paper\n");printf("Smoker 1: Smoking...\n");sleep(1);  // 模拟抽烟时间sem_post(&finish);  // 通知供应者已完成}
}void* smoker2(void* arg) {while (1) {sem_wait(&offer2);  // 等待供应者提供烟草和胶水printf("Smoker 2: Making a cigarette with tobacco and matches\n");printf("Smoker 2: Smoking...\n");sleep(1);  // 模拟抽烟时间sem_post(&finish);  // 通知供应者已完成}
}void* smoker3(void* arg) {while (1) {sem_wait(&offer3);  // 等待供应者提供纸和胶水printf("Smoker 3: Making a cigarette with paper and matches\n");printf("Smoker 3: Smoking...\n");sleep(1);  // 模拟抽烟时间sem_post(&finish);  // 通知供应者已完成}
}int main() {pthread_t smoker1_thread, smoker2_thread, smoker3_thread;// 初始化信号量(假设已经在supplier.c中初始化)extern sem_t offer1, offer2, offer3, finish;// 创建抽烟者线程pthread_create(&smoker1_thread, NULL, smoker1, NULL);pthread_create(&smoker2_thread, NULL, smoker2, NULL);pthread_create(&smoker3_thread, NULL, smoker3, NULL);// 主线程可以继续运行其他任务或等待pthread_join(smoker1_thread, NULL);pthread_join(smoker2_thread, NULL);pthread_join(smoker3_thread, NULL);return 0;
}

代码说明

  1. 信号量初始化

    • supplier.c中初始化信号量offer1offer2offer3finish

    • smoker.c中声明这些信号量为外部变量。

  2. 供应者逻辑

    • 供应者随机选择一组材料并提供。

    • 使用sem_post通知一个抽烟者可以开始卷烟。

    • 使用sem_wait等待抽烟者完成。

  3. 抽烟者逻辑

    • 每个抽烟者等待特定的材料组合。

    • 使用sem_wait等待供应者提供材料。

    • 模拟卷烟和抽烟过程。

    • 使用sem_post通知供应者已完成。

编译与运行

  1. 将上述代码保存为supplier.csmoker.c

  2. 使用以下命令编译代码:

    gcc -pthread -o supplier supplier.c
    gcc -pthread -o smoker smoker.c
  3. 运行程序:

    ./supplier &
    ./smoker

 

输出示例

运行程序后,你将看到类似以下的输出:

Supplier: Offering tobacco and paper
Smoker 1: Making a cigarette with tobacco and paper
Smoker 1: Smoking...
Supplier: Offering tobacco and matches
Smoker 2: Making a cigarette with tobacco and matches
Smoker 2: Smoking...
Supplier: Offering paper and matches
Smoker 3: Making a cigarette with paper and matches
Smoker 3: Smoking...

总结

        这个实现通过信号量控制供应者和抽烟者之间的同步,确保每次只有一个抽烟者能够获取材料并开始卷烟。

相关文章:

  • 【深度解析】DCN-V2:Google新一代特征交叉网络,如何实现推荐系统精准度飞跃?
  • python hasattr()
  • C++基础算法9:Dijkstra
  • Spring AI 实战:第七章、Spring AI Advisor机制之记忆大师
  • 前端面试每日三题 - Day 24
  • 入门Linux 进程:进程概念、进程状态与进程地址空间
  • NPP库中libnppitc模块介绍
  • 49认知干货:产品的生命周期及类型汇总
  • MYSQL数据库突然消失
  • C语言:文件操作
  • 谷歌 NotebookLM 支持生成中文播客
  • 【项目篇之统一硬盘操作】仿照RabbitMQ模拟实现消息队列
  • 【SQL触发器、事务、锁的概念和应用】
  • 计算机网络:详解TCP协议(四次握手三次挥手)
  • Free Draft Model!Lookahead Decoding加速大语言模型解码新路径
  • 主机电路安全防护系统哪个厂家做
  • 2025年01月03日美蜥(杭州普瑞兼职)一面
  • 递归下降算法
  • ART 下 Dex 加载流程源码分析 和 通用脱壳点
  • Linux 进程基础(二):操作系统
  • 禅定佛的微笑,从樊锦诗提到过的那尊说起
  • 国内外数十支搜救犬队伍齐聚三明,进行废墟搜救等实战
  • 解锁川北底色密码,“文化三地”志愿宣讲员招募计划启动报名
  • 浙江一文旅局长五一亲自带团,去年专门考取了导游证
  • 浙江医生举报3岁男童疑遭生父虐待,妇联:已跟爷爷奶奶回家
  • 美法官裁定特朗普援引战时法律驱逐黑帮违法,系首次永久性驳回