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

【操作系统】哲学家进餐问题

问题描述

        哲学家进餐问题是并发编程中的一个经典问题,描述了五位哲学家围坐在一张圆桌旁,他们的生活由思考和进餐组成。在圆桌上有五个盘子,每位哲学家面前一个盘子,盘子之间有一支叉子。哲学家进餐需要同时使用左右两支叉子。问题的核心是如何设计一种算法,使得哲学家们既不会饿死,也不会发生死锁。

问题难点
  1. 资源竞争:每位哲学家需要同时获取两支叉子才能进餐,这可能导致资源竞争。

  2. 死锁:如果每位哲学家都拿起左边的叉子并等待右边的叉子,系统将陷入死锁。

  3. 饥饿:某些哲学家可能长时间无法获取两支叉子,导致饥饿。

解决方案

1. 资源分级方案

为圆桌上的叉子分配一个顺序(例如编号),哲学家必须先拿起编号较小的叉子,再拿起编号较大的叉子。这样可以避免循环等待,从而防止死锁。

2. 限制同时进餐人数

限制最多只有四位哲学家可以尝试进餐,这样至少有一位哲学家可以成功拿起两支叉子并进餐。

3. 使用信号量控制资源

为每支叉子分配一个信号量,并使用信号量来控制叉子的获取和释放。

代码实现

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>#define NUM_PHILOSOPHERS 5// 信号量数组,表示每支叉子
sem_t forks[NUM_PHILOSOPHERS];void* philosopher(void* arg) {int id = *(int*)arg;int left_fork = id;int right_fork = (id + 1) % NUM_PHILOSOPHERS;while (1) {// 思考printf("Philosopher %d is thinking\n", id);sleep(1);// 饿了,尝试拿起叉子printf("Philosopher %d is hungry\n", id);// 确保先拿起编号较小的叉子if (left_fork < right_fork) {// 拿起左边的叉子sem_wait(&forks[left_fork]);printf("Philosopher %d picked up left fork %d\n", id, left_fork);// 拿起右边的叉子sem_wait(&forks[right_fork]);printf("Philosopher %d picked up right fork %d\n", id, right_fork);} else {// 拿起右边的叉子sem_wait(&forks[right_fork]);printf("Philosopher %d picked up right fork %d\n", id, right_fork);// 拿起左边的叉子sem_wait(&forks[left_fork]);printf("Philosopher %d picked up left fork %d\n", id, left_fork);}// 吃饭printf("Philosopher %d is eating\n", id);sleep(1);// 放下右边的叉子sem_post(&forks[right_fork]);printf("Philosopher %d put down right fork %d\n", id, right_fork);// 放下左边的叉子sem_post(&forks[left_fork]);printf("Philosopher %d put down left fork %d\n", id, left_fork);}
}int main() {pthread_t philosophers[NUM_PHILOSOPHERS];int ids[NUM_PHILOSOPHERS];// 初始化信号量for (int i = 0; i < NUM_PHILOSOPHERS; i++) {sem_init(&forks[i], 0, 1);}// 创建哲学家线程for (int i = 0; i < NUM_PHILOSOPHERS; i++) {ids[i] = i;pthread_create(&philosophers[i], NULL, philosopher, &ids[i]);}// 等待线程结束for (int i = 0; i < NUM_PHILOSOPHERS; i++) {pthread_join(philosophers[i], NULL);}// 清理信号量for (int i = 0; i < NUM_PHILOSOPHERS; i++) {sem_destroy(&forks[i]);}return 0;
}

关键修改点

  1. 资源分级

    • 在拿起叉子时,哲学家会检查左右叉子的编号,先拿起编号较小的叉子,再拿起编号较大的叉子。

    • 这样可以确保至少有一个哲学家能够成功拿起两支叉子并进餐,从而打破循环等待条件,避免死锁。

  2. 条件判断

    • 使用if (left_fork < right_fork)来决定先拿起哪支叉子。

    • 如果左边的叉子编号小于右边的叉子编号,则先拿起左边的叉子;否则,先拿起右边的叉子。

http://www.xdnf.cn/news/3838.html

相关文章:

  • 【前缀和】和为 K 的连续子数组
  • 软件检测价格受多种因素影响,你了解多少?
  • 【SAP】FISL的应用
  • 2023华为od机试C卷【跳格子3】
  • 高维亚空间超频物质变压缩技术 第27次CCF-CSP计算机软件能力认证
  • 《应用开发突围指南:敏捷开发的实战精髓》
  • 2001-2021年各城市平均风速数据(可作工具变量)
  • INP指标
  • 【C++贪心 图论】P7903兜心の顶|普及
  • 【算法刷题笔记day one】滑动窗口(定长基础版)
  • Java 反序列化
  • Mybatisplus:一些常用功能
  • ReentrantLock
  • C语言-回调函数
  • 大客户销售大客户营销50个常见概念及其英文表达。AI大客户销售B2B大客户营销关键概念集合
  • 全参数解读Qwen 3 系列模型 + 本地部署实操 + 多维度能力深度测评
  • 计算机总线系统入门:理解数据传输的核心
  • 动态功耗与静态功耗
  • 从零开始理解 C++ 后端编程中的分布式系统
  • Runnable组件重试机制降低程序错误率
  • 深度解析ComfyUI的使用
  • Linux常用命令29——delgroup删除组
  • Spring IoC 注解式开发全解析
  • Java面试资源获取
  • vmware diffy配置ollama 本机ip无法访问
  • AI 大模型常见面试题(及内容解析)
  • ip和域名
  • BUUCTF——禁止套娃
  • 【Hot 100】94. 二叉树的中序遍历
  • Spring 命名空间注入:p、c 与 .util 的深度解析