1226-哲学家进餐

问题描述

https://leetcode-cn.com/problems/the-dining-philosophers/

求解思路

题目没有提供C语言解决方案,只能采用C++,C++有一套自己封装了POSIX的线程库,然而我并不会用,只能还是调用底层的sem_t互斥量及其相关操作集来实现线程同步(又逮到一个C with class)

对于哲学家进餐问题,一共有三种方式避免死锁:

  1. 保证每个哲学家拿叉子互斥,即对哲学家拿起左右两只叉子这个过程上锁保护
  2. 令奇数号哲学家先拿左手边的叉子,偶数号的哲学家先拿右手边的叉子
  3. 至多允许4名哲学家同时吃饭

代码实现

死锁避免方式1:利用mutex保证哲学家同时拿左右两只叉子

#include <semaphore.h>
class DiningPhilosophers {
   public:
    DiningPhilosophers() {
        for (int i = 0; i < 5; ++i) {
            sem_init(&forks[i], 0, 1);
        }
        sem_init(&mutex, 0, 1);
    }
    ~DiningPhilosophers() {
        for (int i = 0; i < 5; ++i) {
            sem_destroy(&forks[i]);
        }
        sem_destroy(&mutex);
    }

    void wantsToEat(int philosopher,
                    function<void()> pickLeftFork,
                    function<void()> pickRightFork,
                    function<void()> eat,
                    function<void()> putLeftFork,
                    function<void()> putRightFork) {
        int leftFork = philosopher;
        int rightFork = (philosopher + 1) % 5;
        sem_wait(&mutex);
        sem_wait(&forks[leftFork]);
        sem_wait(&forks[rightFork]);
        sem_post(&mutex);

        pickLeftFork();
        pickRightFork();

        eat();

        putLeftFork();
        putRightFork();
        sem_post(&forks[leftFork]);
        sem_post(&forks[rightFork]);
    }

   private:
    sem_t forks[5];
    sem_t mutex;
}

死锁避免方式2:奇数号哲学家先拿左手边的叉子,偶数号的哲学家先拿右手边的叉子

#include <semaphore.h>

class DiningPhilosophers {
   public:
    DiningPhilosophers() {
        for (int i = 0; i < 5; ++i) {
            sem_init(&forks[i], 0, 1);
        }
    }
    ~DiningPhilosophers() {
        for (int i = 0; i < 5; ++i) {
            sem_destroy(&forks[i]);
        }
    }

    void wantsToEat(int philosopher,
                    function<void()> pickLeftFork,
                    function<void()> pickRightFork,
                    function<void()> eat,
                    function<void()> putLeftFork,
                    function<void()> putRightFork) {
        int leftFork = philosopher;
        int rightFork = (philosopher + 1) % 5;
        if ((philosopher & 1) == 1) {
            sem_wait(&forks[leftFork]);
            sem_wait(&forks[rightFork]);
            pickLeftFork();
            pickRightFork();
        } else {
            sem_wait(&forks[rightFork]);
            sem_wait(&forks[leftFork]);
            pickRightFork();
            pickLeftFork();
        }

        eat();

        putLeftFork();
        putRightFork();
        sem_post(&forks[leftFork]);
        sem_post(&forks[rightFork]);
    }

   private:
    sem_t forks[5];
};

死锁避免方式3:最多允许4个哲学家同时进餐

#include <semaphore.h>

class DiningPhilosophers {
   public:
    DiningPhilosophers() {
        for (int i = 0; i < 5; ++i) {
            sem_init(&forks[i], 0, 1);
        }
        sem_init(&nums, 0, 4);
    }
    ~DiningPhilosophers() {
        for (int i = 0; i < 5; ++i) {
            sem_destroy(&forks[i]);
        }
        sem_destroy(&nums);
    }

    void wantsToEat(int philosopher,
                    function<void()> pickLeftFork,
                    function<void()> pickRightFork,
                    function<void()> eat,
                    function<void()> putLeftFork,
                    function<void()> putRightFork) {
        int leftFork = philosopher;
        int rightFork = (philosopher + 1) % 5;
        sem_wait(&nums);

        sem_wait(&forks[leftFork]);
        sem_wait(&forks[rightFork]);
        pickLeftFork();
        pickRightFork();

        eat();

        putLeftFork();
        putRightFork();
        sem_post(&forks[leftFork]);
        sem_post(&forks[rightFork]);

        sem_post(&nums);
    }

   private:
    sem_t forks[5];
    sem_t nums;
};

收获和疑惑

C++提供了比函数指针更高级的函数作为参数传递的方式:借助function关键字,有时间深入学习。

有时间学习一下C++封装好的线程库。

上一篇:进程间的五种通信方式


下一篇:高级弥散模型:单指数、IVIM、DKI、SEM、FROC、CTRW