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

news2024/11/26 15:31:46

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

问题示例:

假设有五位哲学家,编号为 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 2

int 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.coloradmin.cn/o/1929920.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Linux 网络配置与连接

一、网络配置 1.1 ifconfig 网卡配置查询 ifconfig #查看所有启动的网络接口信息 ifconfig 指定的网卡 #查看指定网络接口信息 1.2 修改网络配置文件 vim /etc/sysconfig/network-scripts/ifcfg-ens33 #ens33网络配置文…

【ASTGCN】模型调试学习笔记--数据生成详解(超详细)

利用滑动窗口生成时间序列 原理图示&#xff1a; 以PEMS04数据集为例。 该数据集维度为&#xff1a;(16992,307,3)&#xff0c;16992表示时间序列的长度&#xff0c;307为探测器个数&#xff0c;即图的顶点个数&#xff0c;3为特征数&#xff0c;即流量&#xff0c;速度、平…

车间数据采集网关的工作原理和应用场景-天拓四方

在智能制造日益盛行的今天&#xff0c;车间数据采集作为整个生产流程中的关键环节&#xff0c;其重要性愈发凸显。数据采集网关作为这一环节的核心设备&#xff0c;扮演着承上启下的重要角色。本文旨在深入探讨车间数据采集网关的工作原理和应用场景。 一、数据采集网关的工作…

C++:链表插入排序/删除重复节点题解

插入排序 插入排序的思路很简单&#xff0c;基本都知道。 关键是放在链表中&#xff0c; 1.要建立一个哨兵位&#xff0c;这个哨兵位的下一个节点&#xff0c;始终指向val最小的节点。 2.prev指针作为cur的前一个节点&#xff0c;始终指向val最大的节点。它的下一个节点始终…

玩转HarmonyOS NEXT之IM应用首页布局

本文从目前流行的垂类市场中&#xff0c;选择即时通讯应用作为典型案例详细介绍HarmonyOS NEXT的各类布局在实际开发中的综合应用。即时通讯应用的核心功能为用户交互&#xff0c;主要包含对话聊天、通讯录&#xff0c;社交圈等交互功能。 应用首页 创建一个包含一列的栅格布…

Eureka——Spring Cloud中的服务注册与发现组件

目录 1. 前言2. Eureka的概述2.1 Eureka的核心功能2.2 Eureka的角色与特点2.3 Eureka的使用优势 3. 创建 Spring Cloud 的注册中心3.1 创建一个父项目3.2 创建Spring Cloud的注册中心Eureka 4. 创建服务提供者5. 创建一个消费者Consumer&#xff0c;调用服务提供者Provider 1. …

利用OSMnx进行城市路网数据的速度与通行时间推算及分析

本文还是以广州市路网为例&#xff0c;通过osmmx调用ox.add_edge_speeds(G)时&#xff0c;该函数会遍历图G 中的每条边&#xff08;即每条街道&#xff09;&#xff0c;并基于一些预设的规则或街道属性&#xff08;如街道类型、是否为主要道路、是否有速度限制等&#xff09;来…

netdata 监控软件安装与学习

netdata官网 netdata操作文档 前言&#xff1a; netdata是一款监控软件&#xff0c;可以监控多台主机也可以监控单台主机&#xff0c;监控单台主机时&#xff0c;开箱即用&#xff0c;web ui很棒。 环境&#xff1a; [root192 ~]# cat /etc/system-release CentOS Linux rel…

【qt】正则表达式来判断是否为邮箱登录

正则表达式是用来匹配字符串的神器. 在Qt中我们需要使用到QRegExp这个类 用exactMatch来进行匹配. [] 使用方括号 [] 来定义字符类&#xff0c;表示匹配方括号内的任意一个字符 A-Za-z0-9是字符的匹配范围. 是用于指定字符或字符类出现的次数,常见的如下 *&#xff08;匹配 0…

树结构添加分组,向上向下添加同级,添加子级

树结构添加分组&#xff0c;向上向下添加同级&#xff0c;添加子级 效果代码实现页面js 效果 代码实现 页面 <el-tree :data"treeData" :props"defaultProps" :expand-on-click-node"false":filter-node-method"filterNode" :ref&…

pico+unity3d手部动画

在 Unity 开发中&#xff0c;输入系统的选择和运用对于实现丰富的交互体验至关重要。本文将深入探讨 Unity 中的 Input System 和 XR Input Subsystem 这两种不同的输入系统&#xff0c;并详细介绍它们在控制手部动画方面的应用。 一、Input System 和 XR Input Subsystem 的区…

有关电力电子技术的一些相关仿真和分析:⑤交-直-交全桥逆变+全波整流结构电路(MATLAB/Siumlink仿真)

全桥逆变+全波整流结构 参数:Vin=500V, Vo=200V, T=2:1:1, RL=10Ω, fs=100kHz, L=1mH, C=100uF (1)给定输入电压,输出电压和主电路参数,仿真研究电路工作原理,分析工作时序; (2)调节负载电阻,实现电流连续和断续,并仿真验证; (3)调节占空比,分析占空比与电…

【2024开发插件大赛】如何为 ONLYOFFICE 开发插件

我们发布了 2024 插件开发大赛&#xff1a;为 ONLYOFFICE 开发适合中国用户的插件&#xff0c;获得福利与证书。如果您想要参加&#xff0c;阅读本文了解如何为 ONLYOFFICE 开发插件。 关于 ONLYOFFICE ONLYOFFICE 是一个国际开源项目&#xff0c;由领先的 IT 公司 Ascensio Sy…

Microsoft Edge(简称Edge)

Microsoft Edge&#xff08;简称Edge&#xff09;是一款由微软开发的网页浏览器&#xff0c;它为用户提供了许多便捷的功能和选项。以下是Edge浏览器的使用方法&#xff1a; 一、基本使用方法 打开Edge浏览器&#xff1a; 可以在Windows的开始菜单中找到“Microsoft Edge”并点…

Flink Window 窗口【更新中】

Flink Window 窗口 在Flink流式计算中&#xff0c;最重要的转换就是窗口转换Window&#xff0c;在DataStream转换图中&#xff0c;可以发现处处都可以对DataStream进行窗口Window计算。 窗口&#xff08;window&#xff09;就是从 Streaming 到 Batch 的一个桥梁。窗口将无界流…

【数据结构取经之路】二叉搜索树的实现

目录 前言 二叉搜索树 概念 性质 二叉搜索树的实现 结点的定义 插入 查找 删除 二叉搜索树完整代码 前言 首先&#xff0c;二叉搜索树是一种数据结构&#xff0c;了解二叉搜素树有助于理解map和set的特性。 二叉搜索树 概念 二叉搜索树又称二叉排序树&#xff0c…

推荐系统之MIND用户多兴趣网络

目录 引言MIND算法原理1. 算法概述2. 模型结构3. 多兴趣提取层4. 标签感知注意力层 实践应用应用场景1. 电商平台2. 社交媒体3. 视频流媒体4. 内容分发平台 结论 引言 随着大数据和人工智能技术的快速发展&#xff0c;推荐系统已成为电商平台、社交媒体和内容分发平台的重要组成…

如何用python写接口

如何用python写接口&#xff1f;具体步骤如下&#xff1a;  1、实例化server 2、装饰器下面的函数变为一个接口 3、启动服务 开发工具和流程&#xff1a; python库&#xff1a;flask 》实例化server&#xff1a;server flask.Flask(__name__) 》server.route(/index,met…

吃空上千袋,养猫10年经验,生生不息、希喂、弗列加特谁是卷王?

身为宠物医生&#xff0c;我每天都在与猫咪和狗狗的相处中度过&#xff0c;对它们的身体变化十分敏感。当前&#xff0c;许多家养猫面临肥胖和肝脏损伤的双重困扰&#xff0c;虽然医疗手段可以介入&#xff0c;但问题的核心在于宠物主人的喂养方法是否得当。 在我职业生涯的这…

磁盘空间不足java.sql.sQLException:磁盘空间不足

java.sql.sQLException:磁盘空间不足 环境介绍1 查询表空间使用情况2 对表空间文件扩展限制进行修改(或新增表空间数据文件)3 达梦数据库学习使用列表 环境介绍 遇到此错误时,首先查看数据库服务器 , 数据库相关磁盘磁盘空间使用率;在磁盘空间充足的情况下, 业务系统操作达梦数…