问题描述
https://leetcode-cn.com/problems/the-dining-philosophers/
求解思路
题目没有提供C语言解决方案,只能采用C++,C++有一套自己封装了POSIX的线程库,然而我并不会用,只能还是调用底层的sem_t
互斥量及其相关操作集来实现线程同步(又逮到一个C with class)
对于哲学家进餐问题,一共有三种方式避免死锁:
- 保证每个哲学家拿叉子互斥,即对哲学家拿起左右两只叉子这个过程上锁保护
- 令奇数号哲学家先拿左手边的叉子,偶数号的哲学家先拿右手边的叉子
- 至多允许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++封装好的线程库。