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

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

问题描述

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

问题难点
  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)来决定先拿起哪支叉子。

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

相关文章:

  • 赋予网页健壮的灵魂 —— TypeScript(下)
  • ST-LINKV2仿真器下载
  • 基于 AI 的人像修复与编辑技术:CompleteMe 系统的研究与应用
  • 驱动开发硬核特训 · Day 27(下篇):深入掌握 Common Clock Framework 架构与实战开发
  • 如何使用责任链模式优雅实现功能(滴滴司机、家政服务、请假审批等)
  • Python 库 petrel_client.client 浅入浅出
  • Python爬虫(17)反爬攻防战:随机请求头实战指南(fake_useragent库深度解析)
  • python:如何计算皮尔森相关系数
  • 商场防损部绩效考核制度与管理方法
  • Spring MVC常见注解详解
  • JAVA组织/岗位拉取多段时间属性到一张表上时,时间段分隔问题
  • C语言 指针(5)
  • 不定长滑动窗口(求最短/最小)
  • 【quantity】11 体积单位模块(volume.rs)
  • Kubernetes(k8s)学习笔记(五)--部署Ingress实现域名访问和负载均衡
  • C++23 std::tuple与其他元组式对象的兼容 (P2165R4)
  • 每天五分钟深度学习框架PyTorch:基于Dataset封装自定义数据集
  • 用哈希表封装出unordered_set/_map
  • MySQL基础关键_007_DQL 练习
  • MOS管极间电容参数学习
  • 因雷雨、沙尘等天气,这些机场航班运行可能受影响
  • 力保夏粮丰收,粮食大省江苏多地党政主官到田间察看小麦长势
  • 马克思主义理论研究教学名师系列访谈|薛念文:回应时代课题,才能彰显强大生命力
  • 新华每日电讯头版聚焦上海:科创高地向未来
  • 长三角议事厅| AI作曲时代:长三角如何奏响数字音乐乐章
  • 中国科学院院士张泽民已任重庆医科大学校长