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

Linux多线程编程-哲学家就餐问题详解与实现(C语言)

在哲学家就餐问题中,假设有五位哲学家围坐在圆桌前,每位哲学家需要进行思考和进餐两种活动。他们的思考不需要任何资源,但进餐需要使用两根筷子(左右两侧各一根)。筷子是共享资源,哲学家们在进行进餐时需要竞争筷子,而且不能出现死锁情况,即每位哲学家都能在有可能的情况下进餐。

问题示例:

假设有五位哲学家,编号为 0 到 4,围坐在圆桌周围,每位哲学家面前有一根筷子。他们的行为可以描述如下:

  1. 思考:哲学家在没有筷子的时候,思考一段时间。

  2. 进餐:哲学家只有同时拿到左右两边的筷子才能进餐,进餐完成后放下筷子继续思考。

解决方案概述:

  1. 状态记录:每位哲学家有三种状态:思考、饥饿和进餐。
  2. 互斥保护:使用互斥锁保护对状态的访问,确保状态变化的原子性。
  3. 信号量:使用信号量控制每根筷子的使用,确保每位哲学家能同时拿到左右两根筷子。

我们用一个数组 state 来记录每一位哲学家的三个状态,分别是在进餐状态、思考状态、饥饿状态(正在试图拿叉子)。

那么,一个哲学家只有在两个邻居都没有进餐时,才可以进入进餐状态。

第 i 个哲学家的左邻右舍,则由宏 LEFT 和 RIGHT 定义:

  • LEFT : ( i + 5 - 1 ) % 5
  • RIGHT : ( i + 1 ) % 5

比如 i 为 2,则 LEFT 为 1,RIGHT 为 3。

解决步骤:

  1. 定义全局变量和初始化

    • 定义哲学家状态数组 state[],互斥锁 pthread_mutex_t mutex 和信号量数组 sem_t chopsticks[]
    • 初始化互斥锁和每个筷子的信号量。
  2. 哲学家线程函数设计

    • 哲学家线程循环执行思考和进餐过程。
    • 在饥饿状态时,试图获取两侧筷子,获取成功则进餐,否则等待。
    • 进餐后释放筷子,继续思考。
  3. 获取和释放筷子的操作

    • 使用互斥锁保护对状态数组的修改,确保线程安全。
    • 利用信号量控制筷子的获取和释放,避免死锁和资源争夺。

示例代码:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>#define NUM_PHILOSOPHERS 5
#define THINKING 0
#define HUNGRY 1
#define EATING 2int state[NUM_PHILOSOPHERS];  // 哲学家的状态数组
pthread_mutex_t mutex;  // 互斥锁,保护对状态数组的访问
sem_t chopsticks[NUM_PHILOSOPHERS];  // 每根筷子的信号量数组void *philosopher(void *arg) {int id = *((int *)arg);while (1) {// 思考printf("哲学家 %d 正在思考。\n", id);sleep(rand() % 3 + 1);  // 模拟思考时间// 进入饥饿状态printf("哲学家 %d 饿了。\n", id);pthread_mutex_lock(&mutex);state[id] = HUNGRY;printf("哲学家 %d 尝试拿起筷子。\n", id);test(id);  // 尝试获取两侧的筷子pthread_mutex_unlock(&mutex);sem_wait(&chopsticks[id]);  // 获取筷子,如果没有则阻塞// 进餐printf("哲学家 %d 开始进餐。\n", id);sleep(rand() % 3 + 1);  // 模拟进餐时间// 放下筷子sem_post(&chopsticks[id]);  // 放下左侧筷子sem_post(&chopsticks[(id + 1) % NUM_PHILOSOPHERS]);  // 放下右侧筷子printf("哲学家 %d 放下筷子,开始思考。\n", id);}pthread_exit(NULL);
}void test(int id) {if (state[id] == HUNGRY && state[(id + NUM_PHILOSOPHERS - 1) % NUM_PHILOSOPHERS] != EATING&& state[(id + 1) % NUM_PHILOSOPHERS] != EATING) {state[id] = EATING;sem_post(&chopsticks[id]);  // 左侧筷子sem_post(&chopsticks[(id + 1) % NUM_PHILOSOPHERS]);  // 右侧筷子}
}int main() {pthread_t philosophers[NUM_PHILOSOPHERS];int ids[NUM_PHILOSOPHERS];// 初始化互斥锁pthread_mutex_init(&mutex, NULL);// 初始化每根筷子的信号量for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {sem_init(&chopsticks[i], 0, 1);  // 初始值为1,表示筷子可用}// 创建哲学家线程for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {ids[i] = i;pthread_create(&philosophers[i], NULL, philosopher, (void *)&ids[i]);}// 等待线程结束for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {pthread_join(philosophers[i], NULL);}// 清理资源pthread_mutex_destroy(&mutex);for (int i = 0; i < NUM_PHILOSOPHERS; ++i) {sem_destroy(&chopsticks[i]);}return 0;
}

解释和步骤:

1. 全局变量和初始化
  • state[] 数组:每个元素表示一个哲学家的状态,初始为思考状态。
  • pthread_mutex_t mutex:互斥锁,保护对 state[] 数组的访问。
  • sem_t chopsticks[NUM_PHILOSOPHERS] 数组:每根筷子对应一个信号量,初始为可用状态。
2. 哲学家线程函数 philosopher
  • 思考阶段:每个哲学家在思考一段时间后进入饥饿状态。
  • 饥饿阶段:哲学家试图获取左右两根筷子,如果成功则进入进餐状态,否则等待。
  • 进餐阶段:成功获取筷子后进行进餐,一段时间后释放筷子,回到思考阶段。
3. test 函数
  • 检查能否进入进餐状态:检查当前哲学家及其左右邻居的状态,如果都是饥饿状态且两侧筷子可用,则将当前哲学家状态设置为进餐,并释放左右两根筷子。
4. 主函数 main
  • 初始化:初始化互斥锁和每根筷子的信号量。
  • 创建线程:创建并启动每个哲学家的线程。
  • 等待线程结束:主线程等待所有哲学家线程执行完毕。
  • 清理资源:销毁互斥锁和每根筷子的信号量。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【c++刷题笔记-动态规划】day41: 121. 买卖股票的最佳时机、122.买卖股票的最佳时机II 、
  • Perl之正则表达式
  • 【数学建模】技术革新——Lingo的使用超详解
  • 基于Go1.19的站点模板爬虫
  • Vue2中的指令修饰符
  • Linux系统下weblogic10.3.6版本打补丁步骤
  • 最新版康泰克完整版- Kontakt v7.10.5 for Win和Mac,支持m芯片和intel,有入库工具
  • flutter 手写 TabBar
  • 鸿蒙开发:Universal Keystore Kit(密钥管理服务)【查询密钥是否存在(ArkTS)】
  • 东软医疗 踩在中国医疗科技跃迁的风口上
  • 【unity实战】使用unity制作一个红点系统
  • 文件安全传输系统,如何保障信创环境下数据的安全传输?
  • docker 安装 onlyoffice
  • CentOS 7 中出现 cannot open Packages database in /var/lib/rpm 错误
  • 最新PHP自助商城源码,彩虹商城源码
  • 【译】JS基础算法脚本:字符串结尾
  • 【EOS】Cleos基础
  • 【剑指offer】让抽象问题具体化
  • 【刷算法】求1+2+3+...+n
  • C++回声服务器_9-epoll边缘触发模式版本服务器
  • Debian下无root权限使用Python访问Oracle
  • Electron入门介绍
  • input的行数自动增减
  • JAVA之继承和多态
  • Material Design
  • mockjs让前端开发独立于后端
  • nodejs调试方法
  • rc-form之最单纯情况
  • React 快速上手 - 06 容器组件、展示组件、操作组件
  • Vue全家桶实现一个Web App
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 程序员最讨厌的9句话,你可有补充?
  • 复杂数据处理
  • 汉诺塔算法
  • 小程序01:wepy框架整合iview webapp UI
  • 自动记录MySQL慢查询快照脚本
  • shell使用lftp连接ftp和sftp,并可以指定私钥
  • ​Java基础复习笔记 第16章:网络编程
  • ​草莓熊python turtle绘图代码(玫瑰花版)附源代码
  • ​软考-高级-系统架构设计师教程(清华第2版)【第1章-绪论-思维导图】​
  • (1)Map集合 (2)异常机制 (3)File类 (4)I/O流
  • (17)Hive ——MR任务的map与reduce个数由什么决定?
  • (Charles)如何抓取手机http的报文
  • (c语言)strcpy函数用法
  • (Note)C++中的继承方式
  • (超简单)构建高可用网络应用:使用Nginx进行负载均衡与健康检查
  • (二)windows配置JDK环境
  • (附源码)ssm高校运动会管理系统 毕业设计 020419
  • (一)u-boot-nand.bin的下载
  • (原)Matlab的svmtrain和svmclassify
  • (转载)VS2010/MFC编程入门之三十四(菜单:VS2010菜单资源详解)
  • .apk 成为历史!
  • .NET CF命令行调试器MDbg入门(一)
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .NET中两种OCR方式对比