1226 哲学家进餐

探讨了在多线程环境下,如何通过互斥量和信号量解决哲学家就餐问题,确保每位哲学家都能在不发生死锁的情况下交替进行思考与进餐。

题目描述:
5 个沉默寡言的哲学家围坐在圆桌前,每人面前一盘意面。叉子放在哲学家之间的桌面上。(5 个哲学家,5 根叉子)
所有的哲学家都只会在思考和进餐两种行为间交替。哲学家只有同时拿到左边和右边的叉子才能吃到面,而同一根叉子在同一时间只能被一个哲学家使用。每个哲学家吃完面后都需要把叉子放回桌面以供其他哲学家吃面。只要条件允许,哲学家可以拿起左边或者右边的叉子,但在没有同时拿到左右叉子时不能进食。
假设面的数量没有限制,哲学家也能随便吃,不需要考虑吃不吃得下。
设计一个进餐规则(并行算法)使得每个哲学家都不会挨饿;也就是说,在没有人知道别人什么时候想吃东西或思考的情况下,每个哲学家都可以在吃饭和思考之间一直交替下去。
在这里插入图片描述
哲学家从 0 到 4 按 顺时针 编号。请实现函数 void wantsToEat(philosopher, pickLeftFork, pickRightFork, eat, putLeftFork, putRightFork):

philosopher 哲学家的编号。
pickLeftFork 和 pickRightFork 表示拿起左边或右边的叉子。
eat 表示吃面。
putLeftFork 和 putRightFork 表示放下左边或右边的叉子。
由于哲学家不是在吃面就是在想着啥时候吃面,所以思考这个方法没有对应的回调。
给你 5 个线程,每个都代表一个哲学家,请你使用类的同一个对象来模拟这个过程。在最后一次调用结束之前,可能会为同一个哲学家多次调用该函数。

示例:
输入:n = 1
输出:[[4,2,1],[4,1,1],[0,1,1],[2,2,1],[2,1,1],[2,0,3],[2,1,2],[2,2,2],[4,0,3],[4,1,2],[0,2,1],[4,2,2],[3,2,1],[3,1,1],[0,0,3],[0,1,2],[0,2,2],[1,2,1],[1,1,1],[3,0,3],[3,1,2],[3,2,2],[1,0,3],[1,1,2],[1,2,2]]
解释:
n 表示每个哲学家需要进餐的次数。
输出数组描述了叉子的控制和进餐的调用,它的格式如下:
output[i] = [a, b, c] (3个整数)

  • a 哲学家编号。
  • b 指定叉子:{1 : 左边, 2 : 右边}.
  • c 指定行为:{1 : 拿起, 2 : 放下, 3 : 吃面}。
    如 [4,2,1] 表示 4 号哲学家拿起了右边的叉子。

提示:
1 <= n <= 60

方法1:使用5个互斥量来标识5个叉子
题目描述:
(1)使用5个互斥量,标识5个叉子,每个线程,既哲学家,只有同时能够获得两个互斥量,既叉子的情况下,才能够继续执行,既吃面;
(2)若是不能同时获得两个互斥量,则将已经获得的互斥量进行释放,然后再尝试同时获得两个互斥量;

#include<pthread.h>
class DiningPhilosophers {
public:
	//标识五个叉子的互斥量
    pthread_mutex_t mutexs[5];

    DiningPhilosophers() {
        for(int i=0;i<5;++i)//对5个互斥量初始化
            pthread_mutex_init(mutexs+i,0);
    }

    void wantsToEat(int philosopher,
                    function<void()> pickLeftFork,
                    function<void()> pickRightFork,
                    function<void()> eat,
                    function<void()> putLeftFork,
                    function<void()> putRightFork) {
        //当前哲学家需要获得的左右两个叉子
        int left_fork=philosopher;
        int right_fork=(philosopher+1)%5;
        //标识是否获得了左右两个叉子
        int get_left_fork=1;
        int get_right_fork=1;
        while(get_left_fork||get_right_fork){//若是某个叉子没有获得,则再次进入循环
        	//先将之前获得叉子释放
            if(get_left_fork==0)
                pthread_mutex_unlock(mutexs+left_fork);
            if(get_right_fork==0)
                pthread_mutex_unlock(mutexs+right_fork);
            //再次尝试同时获得两个叉子
            get_left_fork= pthread_mutex_trylock(mutexs+left_fork);
            get_right_fork= pthread_mutex_trylock(mutexs+right_fork);
        }
		//跳出循环的时候,表示已经获得两个叉子
        pickLeftFork();
        pickRightFork();
        eat();
        putLeftFork();
        putRightFork();
		//执行完相关操作后,释放叉子
        pthread_mutex_unlock(mutexs+left_fork);
        pthread_mutex_unlock(mutexs+right_fork);
    }
};

方法2:使用互斥量和信号实现
主要思路:
(1)使用大小为5的数组,标识5个叉子,只有同时获得两个叉子的哲学家才能吃饭,既线程才能向下执行;
(2)使用互斥量实现对标识数组的共享,使用信号来通知数组的可获得状态;

#include<pthread.h>
class DiningPhilosophers {
public:
    pthread_mutex_t mutex;
    pthread_cond_t cond;
    bool is_using[5];//标识5个叉子
    DiningPhilosophers() {
    	//初始化
        for(int i=0;i<5;++i)
            is_using[i]=false;
        pthread_mutex_init(&mutex,0);
        pthread_cond_init(&cond,0);
    }

    void wantsToEat(int philosopher,
                    function<void()> pickLeftFork,
                    function<void()> pickRightFork,
                    function<void()> eat,
                    function<void()> putLeftFork,
                    function<void()> putRightFork) {
         //当前哲学家需要获得两个叉子编号
        int left_fork=philosopher;
        int right_fork=(philosopher+1)%5;
		//加锁,保证只有一个线程能获得标识叉子的数组
        pthread_mutex_lock(&mutex);
        //只有同时获得两个叉子,才能吃饭
        //既需要该线程在获得锁的时刻,两个叉子都没有被被其他线程使用
        while(is_using[left_fork]||is_using[right_fork]){
            pthread_cond_wait(&cond,&mutex);
        }
        //将标识两个叉子的数组元素的状态改变
		is_using[left_fork]=true;
        is_using[right_fork]=true;
        pthread_mutex_unlock(&mutex);//解锁,便于其他线程接着获取其他叉子
        //执行相关操作
        pickLeftFork();
        pickRightFork();
        eat();
        putLeftFork();
        putRightFork();
        //再次加锁,以获得对标识叉子的数组的独享,然后修改对应的叉子状态,既不再需要这个叉子
        pthread_mutex_lock(&mutex);
        is_using[left_fork]=false;
        is_using[right_fork]=false;
        pthread_mutex_unlock(&mutex);//解锁
        pthread_cond_signal(&cond);//通知释放了两个锁
    }
};
Delphi 12.3 作为一款面向 Windows 平台的集成开发环境,由 Embarcadero Technologies 负责其持续演进。该环境以 Object Pascal 语言为核心,并依托 Visual Component Library(VCL)框架,广泛应用于各类桌面软件、数据库系统及企业级解决方案的开发。在此生态中,Excel4Delphi 作为一个重要的社区开源项目,致力于搭建 Delphi 与 Microsoft Excel 之间的高效桥梁,使开发者能够在自研程序中直接调用 Excel 的文档处理、工作表管理、单元格操作及宏执行等功能。 该项目以库文件与组件包的形式提供,开发者将其集成至 Delphi 工程后,即可通过封装良好的接口实现对 Excel 的编程控制。具体功能涵盖创建与编辑工作簿、格式化单元格、批量导入导出数据,乃至执行内置公式与宏指令等高级操作。这一机制显著降低了在财务分析、报表自动生成、数据整理等场景中实现 Excel 功能集成的技术门槛,使开发者无需深入掌握 COM 编程或 Excel 底层 API 即可完成复杂任务。 使用 Excel4Delphi 需具备基础的 Delphi 编程知识,并对 Excel 对象模型有一定理解。实践中需注意不同 Excel 版本间的兼容性,并严格遵循项目文档进行环境配置与依赖部署。此外,操作过程中应遵循文件访问的最佳实践,例如确保目标文件未被独占锁定,并实施完整的异常处理机制,以防数据损毁或程序意外中断。 该项目的持续维护依赖于 Delphi 开发者社区的集体贡献,通过定期更新以适配新版开发环境与 Office 套件,并修复已发现的问题。对于需要深度融合 Excel 功能的 Delphi 应用而言,Excel4Delphi 提供了经过充分测试的可靠代码基础,使开发团队能更专注于业务逻辑与用户体验的优化,从而提升整体开发效率与软件质量。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值