哲学家就餐问题C语言实现(涉及多线程、信号量等)

哲学家就餐问题C语言实现(涉及多线程、信号量等)

1、实验原理

  由Dijkstra提出并解决的哲学家进餐问题(The Dinning Philosophers Problem)是典型的同步问题。该问题是描述有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕,放下筷子继续思考。

2、实验内容

  显示出每个哲学家的工作状态,如吃饭,思考。连续运行30次以上都未出现死锁现象。

3、代码部分

3.1 重要代码展示

重要代码展示:
(1)初始化int型数组ID[NUM]标识线程ID号,用于后续传参;初始化信号量数组sem_chopsticks[NUM]模拟五根筷子;初始化信号量sem_eaters=4,用来控制同时可拿起筷子的最大人数。

int ID[NUM]={0,1,2,3,4};
sem_t sem_chopsticks[NUM];
sem_t sem_eaters;
sem_init(&sem_eaters,0,NUM-1)

(2)线程的例程处理函数,先等待信号量sem_eaters,再等待信号量sem_chopsticks[pthread_id],这两个信号量不为零说明此时同时拿起筷子的人数小于4,且该哲学家编号相同的筷子(记为左筷子)可以被拿起,所以拿起左子,然后等待右筷子sem_chopsticks[pthread_id+1]信号量,拿到右筷子后便可以进餐,随机释放上述三个信号量。

void philosopher(void * ptid){
    int pthread_id = *(int *)ptid%NUM;
    printf("%d philosopher is thinking...\n",(int)pthread_id);
    sem_wait(&sem_eaters);
    sem_wait(&sem_chopsticks[pthread_id]);
    printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,(int)pthread_id);
    sem_wait(&sem_chopsticks[(pthread_id+1)%NUM]);
    printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,((int)pthread_id+1)%NUM);
printf("%d philosopher is eating, %d philosopher had already dined.\n",(int)pthread_id,eaters_num);
    sem_post(&sem_chopsticks[(pthread_id+1)%NUM]);
    sem_post(&sem_chopsticks[pthread_id]);
    sem_post(&sem_eaters);
    eaters_num++;
    printf("%d philosopher had dined, by now %d philosopher had already dined.\n",(int)pthread_id,eaters_num);}

(3)循环创建5个线程模拟五位哲学家,线程例子程函数为philosopher,并传入参数ID[i]标识线程的ID。

pthread_t philosopher_threads[NUM];
        sem_signal_init();
        for ( i= 0; i < NUM; i++) {
            printf("%d times\n",i);
            if (pthread_create(&philosopher_threads[i], NULL, (void *)&philosopher,&ID[i]) != 0){
                perror("oops:pthread_create error!");
                exit(1);
            }

3.2 源码

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

#define NUM 5
int ID[NUM]={0,1,2,3,4};
sem_t sem_chopsticks[NUM];
sem_t sem_eaters;
int eaters_num=0;
void sem_signal_init(){
    int i;
    for (i=0; i<NUM; i++){
        if (sem_init(&sem_chopsticks[i],0,1) == -1){
            perror("oops:em_init error!");
            exit(1);
        }
    }
    if (sem_init(&sem_eaters,0,NUM-1) == -1){
        perror("oops:em_init error!");
        exit(1);
    }
}

void philosopher(void * ptid){
    int pthread_id = *(int *)ptid%NUM;
    printf("%d philosopher is thinking...\n",(int)pthread_id);
    sem_wait(&sem_eaters);
    sem_wait(&sem_chopsticks[pthread_id]);
    printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,(int)pthread_id);
    sem_wait(&sem_chopsticks[(pthread_id+1)%NUM]);
    printf("%d philosopher takes chopstick %d...\n",(int)pthread_id,((int)pthread_id+1)%NUM);
    printf("%d philosopher is eating, %d philosopher had already dined.\n",(int)pthread_id,eaters_num);
    sem_post(&sem_chopsticks[(pthread_id+1)%NUM]);
    sem_post(&sem_chopsticks[pthread_id]);
    sem_post(&sem_eaters);
    eaters_num++;
    printf("%d philosopher had dined, by now %d philosopher had already dined.\n",(int)pthread_id,eaters_num);

}

int main(){
    int i,l,j,k;
    for (l = 0; l < 1000; ++l) {
        printf("**********************%d times try ******************************",l+1);
        pthread_t philosopher_threads[NUM];
        sem_signal_init();
        for ( i= 0; i < NUM; i++) {
            printf("%d times\n",i);
            if (pthread_create(&philosopher_threads[i], NULL, (void *)&philosopher,&ID[i]) != 0){
                perror("oops:pthread_create error!");
                exit(1);
            }
        }

        for ( j = 0; j < NUM; j++) {
            pthread_join(philosopher_threads[j], NULL);
        }
        sem_destroy(&sem_eaters);
        for (k = 0; k < NUM ; ++k) {
            sem_destroy(&sem_chopsticks[k]);
        }
        eaters_num = 0;
//        sleep(2);
    }

    return 0;
}

3.3 运行结果展示

  本程序用五个线程模拟五位哲学家,用五个sem_t型信号量模拟五根筷子(临界资源),解题的思想是:每次让每位哲学家优先拿起自己左边的筷子,并限制最多有四个人同时拿起筷子,从而避免了死锁情况,该程序模拟五个哲学家进餐一次的情况,并重复模拟了1000次以上来检验是否出现死锁情况,经检验程序运行正常,没有出现死锁状况,上述图片仅展示了第1次和第1000次的情况。

在这里插入图片描述

  • 24
    点赞
  • 188
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
以下是使用信号量解决哲学家进餐问题的 C 语言实现: ``` #include <stdio.h> #include <stdlib.h> #include <pthread.h> #include <semaphore.h> #define N 5 // 哲学家数量 #define LEFT (i + N - 1) % N // 左边的哲学家 #define RIGHT (i + 1) % N // 右边的哲学家 sem_t chopstick[N]; // 餐叉信号量 sem_t mutex; // 互斥信号量 void *philosopher(void *arg) { int i = *((int *) arg); while (1) { // 思考 printf("Philosopher %d is thinking\n", i); sleep(rand() % 5); // 获取两个相邻的餐叉 printf("Philosopher %d is hungry\n", i); sem_wait(&mutex); sem_wait(&chopstick[LEFT]); sem_wait(&chopstick[RIGHT]); sem_post(&mutex); // 就餐 printf("Philosopher %d is eating\n", i); sleep(rand() % 5); // 释放两个餐叉 sem_post(&chopstick[LEFT]); sem_post(&chopstick[RIGHT]); } } int main() { pthread_t tid[N]; int i; // 初始化信号量 sem_init(&mutex, 0, 1); for (i = 0; i < N; i++) { sem_init(&chopstick[i], 0, 1); } // 创建哲学家线程 for (i = 0; i < N; i++) { pthread_create(&tid[i], NULL, philosopher, &i); } // 等待哲学家线程结束 for (i = 0; i < N; i++) { pthread_join(tid[i], NULL); } // 销毁信号量 sem_destroy(&mutex); for (i = 0; i < N; i++) { sem_destroy(&chopstick[i]); } return 0; } ``` 在上面的代码中,我们创建了 N 个哲学家线程,每个哲学家线程都循环执行思考、就餐和释放餐叉的操作。在获取餐叉时,我们使用了互斥信号量 mutex 来保证同一时间只有一个哲学家能够获取餐叉,避免死锁和饥饿等问题。同时,每个哲学家都有两个相邻的餐叉,分别为左边的餐叉和右边的餐叉,我们使用了 chopstick 数组来表示这些餐叉的信号量。在获取餐叉时,先获取互斥信号量 mutex,然后再获取左边和右边的餐叉信号量,这样就能保证每个哲学家都能安全地获取到两个相邻的餐叉,避免死锁和饥饿等问题。在就餐完成后,释放餐叉时,也需要先释放互斥信号量 mutex,然后再释放左边和右边的餐叉信号量。最后,我们在主函数中创建哲学家线程,等待哲学家线程结束,然后销毁信号量

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

那由塔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值