哲学家就餐问题

问题

在一个圆桌上坐着五位哲学家,每个哲学家面前有一个碗装有米饭的碗和一个筷子。哲学家的生活包括思考和进餐两个活动。当一个哲学家思考时,他不需要任何资源。当他饿了时,他试图拿起两只相邻的筷子来吃饭。由于碗和筷子不能被共享,因此必须找到一种方法来确保哲学家能够安全地进餐,即不会发生死锁(所有哲学家都在等待筷子)也不会发生饥饿(某些哲学家永远无法拿起筷子)

信号量实现

发生死锁版

#include <pthread.h>
#include <semaphore.h>
#include <vector>
#include <unistd.h>
#include <iostream>

#define N 5
#define P(x) sem_wait(x)
#define V(x) sem_post(x)

sem_t mutex_table;
std::vector<sem_t> mutex_chopsticks(N);


void* T_philosopher(void* arg) {
    int id = *((int*)arg);

    // id哲学家吃饭需要的2根筷子
    int lhs = (id + N - 1) % N;
    int rhs = id % N;

    while (true) {
        P(&mutex_chopsticks[lhs]);
        printf("+ %d by T%d\n", lhs, id);
        P(&mutex_chopsticks[rhs]);
        printf("+ %d by T%d\n", rhs, id);

        // Eat.
        // Philosophers are allowed to eat in parallel.

        printf("- %d by T%d\n", lhs, id);
        printf("- %d by T%d\n", rhs, id);
        V(&mutex_chopsticks[lhs]);
        V(&mutex_chopsticks[rhs]);

        sleep(0.5);
    }
}

int main() {
    for (int i = 0; i < N; i++) {
        sem_init(&mutex_chopsticks[i], 0, 1);
    }
    std::vector<pthread_t> threads(N);
    std::vector<int> ids(N);
    for (int i = 0; i < N; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], nullptr, &T_philosopher, &ids[i]);
    }
    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], nullptr);
    }
}

限制人数版

限制最多只能4人同时上桌, 则可以保证至少有一个人可以同时拿起两根筷子

#include <pthread.h>
#include <semaphore.h>
#include <vector>
#include <unistd.h>
#include <iostream>

#define P(x) sem_wait(x)
#define V(x) sem_post(x)

const int N = 5;

sem_t mutex_table;
std::vector<sem_t> mutex_chopsticks(N);


void* T_philosopher(void* arg) {
    int id = *((int*)arg);

    // id哲学家吃饭需要的2根筷子
    int lhs = (id + N - 1) % N;
    int rhs = id % N;

    while (true) {
        // Come to mutex_table
        P(&mutex_table);
        P(&mutex_chopsticks[lhs]);
        printf("+ %d by T%d\n", lhs, id);
        P(&mutex_chopsticks[rhs]);
        printf("+ %d by T%d\n", rhs, id);

        // Eating
        // Philosophers are allowed to eat in parallel.

        printf("- %d by T%d\n", lhs, id);
        printf("- %d by T%d\n", rhs, id);
        V(&mutex_chopsticks[lhs]);
        V(&mutex_chopsticks[rhs]);

        // Leave mutex_table
        V(&mutex_table);
        // Thinking
        sleep(0.5);
    }
}

int main() {
    // 保证任何实际最多只有4个人在桌子上
    // 这样至少有1个人可以拿到2根筷子
    sem_init(&mutex_table, 0, N - 1);

    for (int i = 0; i < N; i++) {
        sem_init(&mutex_chopsticks[i], 0, 1);
    }
    std::vector<pthread_t> threads(N);
    std::vector<int> ids(N);
    for (int i = 0; i < N; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], nullptr, &T_philosopher, &ids[i]);
    }
    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], nullptr);
    }
}

规定取筷顺序

规定每个人, 先拿编号小的筷子, 再拿编号大的筷子.

#include <pthread.h>
#include <semaphore.h>
#include <vector>
#include <unistd.h>
#include <iostream>

#define P(x) sem_wait(x)
#define V(x) sem_post(x)

const int N = 5;

sem_t mutex_table;
std::vector<sem_t> mutex_chopsticks(N);


void* T_philosopher(void* arg) {
    int id = *((int*)arg);

    // id哲学家吃饭需要的2根筷子
    int lhs = (id + N - 1) % N;
    int rhs = id % N;

    while (true) {
        // 规定每个人, 先拿编号小的筷子, 再拿编号大的筷子
        if (lhs < rhs) {
            P(&mutex_chopsticks[lhs]);
            P(&mutex_chopsticks[rhs]);
        } else {
            P(&mutex_chopsticks[rhs]);
            P(&mutex_chopsticks[lhs]);
        }
        printf("+ %d by T%d\n", lhs, id);
        printf("+ %d by T%d\n", rhs, id);

        // Eating

        printf("- %d by T%d\n", lhs, id);
        printf("- %d by T%d\n", rhs, id);
        V(&mutex_chopsticks[lhs]);
        V(&mutex_chopsticks[rhs]);

        // Thinking
        sleep(0.5);
    }
}

int main() {
    for (int i = 0; i < N; i++) {
        sem_init(&mutex_chopsticks[i], 0, 1);
    }
    std::vector<pthread_t> threads(N);
    std::vector<int> ids(N);
    for (int i = 0; i < N; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], nullptr, &T_philosopher, &ids[i]);
    }
    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], nullptr);
    }
}


条件变量实现


数组available[i]表示第i根筷子是否可用, 初始N根筷子都是可用的.

互斥锁用来保证每次只有一个线程检查available数组

当一个线程检查到对应的左右筷子都是可用时, 吃.

否则等待其它线程释放筷子后唤醒.

#include <pthread.h>
#include <vector>
#include <unistd.h>
#include <iostream>

#define Lock(x) pthread_mutex_lock(x)
#define UnLock(x) pthread_mutex_unlock(x)

const int N = 5;

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
std::vector<int> available(N, true);

void* T_philosopher(void* arg) {
    int id = *((int*)arg);

    // id哲学家吃饭需要的2根筷子
    int lhs = (id + N - 1) % N;
    int rhs = id % N;

    while (true) {
        // Come to mutex_table
        Lock(&mutex);
        while (!(available[lhs] && available[rhs])) {
            pthread_cond_wait(&cond, &mutex);
        }
        printf("+ %d by T%d\n", lhs, id);
        printf("+ %d by T%d\n", rhs, id);
        available[lhs] = available[rhs] = false;
        UnLock(&mutex);

        
        
        // Eating
        sleep(0.5);

        printf("- %d by T%d\n", lhs, id);
        printf("- %d by T%d\n", rhs, id);
        available[lhs] = available[rhs] = true;
        pthread_cond_broadcast(&cond);

        // Thinking
        sleep(0.5);
    }
}

int main() {
    std::vector<pthread_t> threads(N);
    std::vector<int> ids(N);
    for (int i = 0; i < N; i++) {
        ids[i] = i + 1;
        pthread_create(&threads[i], nullptr, &T_philosopher, &ids[i]);
    }
    for (int i = 0; i < N; i++) {
        pthread_join(threads[i], nullptr);
    }
}
  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个问题的解决方案涉及到一个叫做“隐形拆解”的概念,它是一种把一个复杂的问题分解成许多更小的问题,然后一步步解决它们。因此,哲学家就餐问题的解决代码就是一步步解决这些更小的问题,从而获得最终的解决方案。 ### 回答2: 哲学家进餐问题是一个经典的并发同步问题,涉及到有限资源的竞争使用。问题描述如下:有五个哲学家,围坐在一张圆桌周围,每个哲学家面前都有一碗面、一只叉子和一把刀。哲学家只有在同时拿起左右两把叉子时,才能进餐。但每个叉子一次只能被一个哲学家使用。如果采用简单的解决方案,容易出现死锁,即每个哲学家都在等待对方放下其中的一支叉子才能进餐。 为了解决这个问题,可以采用以下的解决代码: 1. 定义五个哲学家对象和五个叉子对象作为共享资源。 2. 使用信号量来表示每个叉子的状态,初始时所有叉子的状态为可用。 3. 每个哲学家的进餐行为由一个线程来表示,每个线程的执行顺序被随机化,避免死锁。 4. 在每个哲学家的线程中,首先尝试获取左边的叉子,如果获取成功,则尝试获取右边的叉子。如果获取失败,则释放已经获取的叉子并等待一段时间后再进行尝试。 5. 当一个哲学家成功获取两只叉子时,他可以进行进餐,进餐一段时间后释放叉子,然后等待一段时间后再进行下一次进餐。 通过以上的解决方法,哲学家的进餐问题可以得到合理的解决。每个哲学家都有机会进餐,并且不会出现死锁的情况。这是一种基于资源互斥的解决方案,通过合理的资源分配和释放,实现了对有限资源的合理使用。 ### 回答3: 哲学家进餐问题是一个经典的并发编程问题。该问题描述了五位哲学家围坐在圆桌周围,每人面前有一个碗和一双筷子。这五位哲学家交替进行思考和就餐的活动。但是,由于每位哲学家就餐时需要同时拿起其左右两边的筷子,而且筷子不能被共享,因此可能会发生死锁。 为了解决这个问题,可以采用以下代码: 1. 定义一个互斥锁数组,长度与哲学家的数量相同,即五位。 2. 定义一个条件变量数组,长度与哲学家的数量相同。 3. 每位哲学家的行为可以被建模为一个线程。 4. 每位哲学家在思考时,首先会尝试获取左右两边的筷子。如果筷子已经被其他哲学家拿走,则该哲学家会进入等待状态。 5. 当哲学家成功获得左右两边的筷子后,开始就餐。 6. 只有当一个哲学家就餐完成后,他才会释放左右两边的筷子,让其他哲学家可以使用。 7. 当一个哲学家释放筷子后,会唤醒正在等待的其他哲学家。 8. 哲学家的线程循环执行思考和就餐的过程。 这个解决方案确保每位哲学家能够在获取到所有必需的资源后再就餐,并避免了死锁的发生。同时,通过使用互斥锁和条件变量,可以确保只有当资源可用时才会触发相应的操作。 虽然这只是一个简单示例,但是哲学家进餐问题充分体现了并发编程中常见的资源竞争和死锁等问题,因此对于理解并发编程的基本原则和应用非常有帮助。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值