当前位置: 首页 > 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
  • 初始化:初始化互斥锁和每根筷子的信号量。
  • 创建线程:创建并启动每个哲学家的线程。
  • 等待线程结束:主线程等待所有哲学家线程执行完毕。
  • 清理资源:销毁互斥锁和每根筷子的信号量。

http://www.lryc.cn/news/401119.html

相关文章:

  • 从C向C++18——演讲比赛流程管理系统
  • QThread和std::thread
  • LeetCode 算法:组合总和 c++
  • 【两大3D转换SDK对比】HOOPS Exchange VS. CAD Exchanger
  • Openerstry + lua + redis根据请求参数实现动态路由转发
  • 数字名片-Pushmall 智能AI数字名片7月更新计划
  • 21. Python代码快速查看数组分布
  • 记录些Redis题集(3)
  • OracleLinux6.9升级UEK内核
  • React学习笔记03-----手动创建和运行
  • ubantu22.04安装OceanBase 数据库
  • 【linux】【深度学习】fairseq框架安装踩坑
  • 【Python爬虫教程】第7篇-requests模块的cookies保存和使用
  • 微信小程序开发基础知识6----使用npm包
  • 如何在element中table的 v-for中 使用slot-scope?
  • 企业网络实验dhcp-snooping、ip source check,防非法dhcp服务器、自动获取ip(虚拟机充当DHCP服务器)、禁手动修改IP
  • 20. Python读取.mat格式文件通用函数
  • Cypress UI自动化之安装环境
  • SpringApplication.java类
  • 智能招聘系统的AI功能解析
  • AV1技术学习:Translational Motion Compensation
  • mysql中的存储过程
  • 07:串口通信二
  • 识别视频中的人数并统计出来
  • 【TypeDB 】机器学习和符号 AI 在机器人技术中的作用
  • EPLAN 去掉PDF中的红色跳转标识
  • 【car】深入浅出学习机械燃油车知识、结构、原理、维修、保养、改装、编程
  • 语音识别概述
  • 勒索防御第一关 亚信安全AE防毒墙全面升级 勒索检出率提升150%
  • elementui 日历组件el-calendar使用总结