哲学家就餐问题:互斥锁和信号量解决方案

哲学家就餐问题是一个经典的并发编程问题,用来演示资源竞争和死锁的可能性。问题描述如下:

5 个哲学家围着圆桌坐,全体到达后开始讨论。在讨论的间隙哲学家进餐,每人进餐时都需使用一双筷子,所有哲学家左右手筷子都拿到后才能进餐。哲学家的人数、餐桌上的布置自行设定,实现筷子的互斥使用算法的程序实现。

本文将使用 C 语言实现哲学家就餐问题,并提供两种解决方案:互斥锁和信号量。

使用互斥锁解决哲学家就餐问题

算法描述:

  1. 创建一个互斥锁数组,用于表示每个筷子的状态。
  2. 每个哲学家进程需要获取左右两支筷子的互斥锁才能进餐。
  3. 当哲学家吃完饭后,释放两支筷子的互斥锁。

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#define N 5 // 哲学家的数量

pthread_mutex_t forks[N]; // 筷子的互斥锁

void *philosopher(void *arg) {
    int num = *(int*)arg; // 哲学家的编号
    int left = num; // 左手的筷子编号
    int right = (num + 1) % N; // 右手的筷子编号
    
    while (1) {
        // 哲学家思考
        printf('哲学家 %d 开始思考\n', num);
        sleep(rand() % 3);
        
        // 哲学家饿了,准备就餐
        printf('哲学家 %d 饿了,准备就餐\n', num);
        
        // 先尝试获取左手的筷子
        pthread_mutex_lock(&forks[left]);
        printf('哲学家 %d 拿起了左手的筷子 %d\n', num, left);
        
        // 再尝试获取右手的筷子
        if (pthread_mutex_trylock(&forks[right]) == 0) {
            printf('哲学家 %d 拿起了右手的筷子 %d\n', num, right);
            printf('哲学家 %d 开始进餐\n', num);
            
            // 哲学家进餐
            sleep(rand() % 3);
            
            // 哲学家就餐完毕,释放筷子
            printf('哲学家 %d 进餐完毕,放下筷子 %d 和 %d\n', num, left, right);
            pthread_mutex_unlock(&forks[left]);
            pthread_mutex_unlock(&forks[right]);
        } else {
            // 右手的筷子被其他哲学家占用,放下左手的筷子
            printf('哲学家 %d 拿起右手的筷子 %d 失败\n', num, right);
            printf('哲学家 %d 放下左手的筷子 %d\n', num, left);
            pthread_mutex_unlock(&forks[left]);
        }
    }
    
    return NULL;
}

int main() {
    pthread_t philosophers[N];
    int nums[N] = {0, 1, 2, 3, 4}; // 哲学家的编号
    
    // 初始化互斥锁
    for (int i = 0; i < N; i++) {
        pthread_mutex_init(&forks[i], NULL);
    }
    
    // 创建哲学家进程
    for (int i = 0; i < N; i++) {
        pthread_create(&philosophers[i], NULL, philosopher, (void*)&nums[i]);
    }
    
    // 等待哲学家进程结束
    for (int i = 0; i < N; i++) {
        pthread_join(philosophers[i], NULL);
    }
    
    // 销毁互斥锁
    for (int i = 0; i < N; i++) {
        pthread_mutex_destroy(&forks[i]);
    }
    
    return 0;
}

使用信号量解决哲学家就餐问题

算法描述:

  1. 创建一个信号量数组,用于表示每个筷子的状态。初始值为 1,表示每个筷子可用。
  2. 每个哲学家进程需要获取左右两支筷子的信号量才能进餐。
  3. 当哲学家吃完饭后,释放两支筷子的信号量。

代码实现:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>

#define N 5 // 哲学家的数量

sem_t forks[N]; // 筷子的信号量

void *philosopher(void *arg) {
    int num = *(int*)arg; // 哲学家的编号
    int left = num; // 左手的筷子编号
    int right = (num + 1) % N; // 右手的筷子编号
    
    while (1) {
        // 哲学家思考
        printf('哲学家 %d 开始思考\n', num);
        sleep(rand() % 3);
        
        // 哲学家饿了,准备就餐
        printf('哲学家 %d 饿了,准备就餐\n', num);
        
        // 先尝试获取左手的筷子
        sem_wait(&forks[left]);
        printf('哲学家 %d 拿起了左手的筷子 %d\n', num, left);
        
        // 再尝试获取右手的筷子
        if (sem_trywait(&forks[right]) == 0) {
            printf('哲学家 %d 拿起了右手的筷子 %d\n', num, right);
            printf('哲学家 %d 开始进餐\n', num);
            
            // 哲学家进餐
            sleep(rand() % 3);
            
            // 哲学家就餐完毕,释放筷子
            printf('哲学家 %d 进餐完毕,放下筷子 %d 和 %d\n', num, left, right);
            sem_post(&forks[left]);
            sem_post(&forks[right]);
        } else {
            // 右手的筷子被其他哲学家占用,放下左手的筷子
            printf('哲学家 %d 拿起右手的筷子 %d 失败\n', num, right);
            printf('哲学家 %d 放下左手的筷子 %d\n', num, left);
            sem_post(&forks[left]);
        }
    }
    
    return NULL;
}

int main() {
    pthread_t philosophers[N];
    int nums[N] = {0, 1, 2, 3, 4}; // 哲学家的编号
    
    // 初始化信号量
    for (int i = 0; i < N; i++) {
        sem_init(&forks[i], 0, 1);
    }
    
    // 创建哲学家进程
    for (int i = 0; i < N; i++) {
        pthread_create(&philosophers[i], NULL, philosopher, (void*)&nums[i]);
    }
    
    // 等待哲学家进程结束
    for (int i = 0; i < N; i++) {
        pthread_join(philosophers[i], NULL);
    }
    
    // 销毁信号量
    for (int i = 0; i < N; i++) {
        sem_destroy(&forks[i]);
    }
    
    return 0;
}

总结

本文介绍了使用 C 语言实现哲学家就餐问题,并提供两种解决方案:互斥锁和信号量。两种方法都能够有效地避免死锁问题,但信号量方法更加灵活,可以用于更加复杂的同步问题。

注意:

  • 以上代码仅供参考,实际应用中需要根据具体情况进行修改。
  • 哲学家就餐问题是一个经典的并发编程问题,需要深入理解才能更好地应用于实际项目。
用 C 语言实现哲学家就餐问题:互斥锁和信号量解决方案

原文地址: https://www.cveoy.top/t/topic/o4Go 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录