Linux系统编程:哲学家问题

Description

有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。

分析

该问题涉及Linux系统编程中经典的线程同步问题。
本篇采用信号量sem来实现。
信号量sem相当于进阶版的mutex,sem_wait()相当于sem–,sem_post相当于sem++。,定义多个信号量,其中筷子用初始化为1的sem来实现,保证其只能被一个哲学家拿起,要注意的是,为了防止四个哲学家同时拿起左手或者右手的筷子而导致死锁,我们需要定义一个sem数组,表示最多拿起左手或右手的哲学家数量为4,如此来避免死锁问题。
最后,利用srand函数和rand函数来保证哲学家拿起左右筷子的随机性。

代码

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/types.h>
#include <pthread.h>
#include <semaphore.h>

#define NUM 5                   //5位哲学家
sem_t max[2];                 //最多允许4位哲学家同时拿起左手或右手筷子(0为右手,1为左手)
sem_t chopsticks[NUM];          //信号量模拟筷子

void my_err(const char *str)
{
     perror(str);
     exit(1);
}

void *philosopher(void *arg)
{
    int i = (long)arg;
    int left = i;               //左手筷子定义为i
    int right = (i+1) % NUM;    //右手筷子定义为i+1,若i == 4, right = 0
    int first;                  //第一次是左手还是右手?
    int second;                 //第二次?
    int is_first_left;          

    while (1)
    {
        //思考时间
        printf("-----哲学家%d思考中\n", i);
        sleep(rand() % 5 + 1);  //让他思考那么1-5s
        //随机左右手
        if (is_first_left = (rand() % 2))
        {
            first = left;
            second = right;
        }
        else
        {
            first = right;
            second = left;
        }
        //拿第一只筷子
        sem_wait(&max[is_first_left]);
        sem_wait(&chopsticks[first]);
        if (is_first_left)         
            printf("=====第一次,哲学家%d拿起了左手的筷子%d\n", i, left);
        else
            printf("=====第一次,哲学家%d拿起了右手的筷子%d\n", i, right);
        //第二只
        sem_wait(&max[!is_first_left]);
        sem_wait(&chopsticks[second]);
        if (is_first_left)         
            printf("=====第二次,哲学家%d拿起了右手的筷子%d\n", i, right);
        else
            printf("=====第二次,哲学家%d拿起了左手的筷子%d\n", i, left);
        //用餐时间
        printf("-----哲学家%d用餐中\n", i);
        sleep(rand() % 5 + 1);
        //放筷子吧
        sem_post(&chopsticks[left]);
        sem_post(&max[1]);
        printf("=====哲学家%d放下了左手的筷子%d\n", i, left);
        sem_post(&chopsticks[right]);
        sem_post(&max[0]);
        printf("=====哲学家%d放下了右手的筷子%d\n", i, right);
    }
}

int main(int argc, char *argv[])
{
    srand( time(NULL) );

    pthread_t pid[NUM];
    //初始化
    for (int i = 0; i < 2; i++)
        sem_init(&max[i], 0, 4);
    for (int i = 0; i < 5; i++)
    {
        sem_init(&chopsticks[i], 0, 1);
    }
    //创建哲学家
    for (int i = 0; i < 5; i++)
    {
        pthread_create(&pid[i], NULL, philosopher, (void*)i);
    }
    //销毁
    for (int i = 0; i < 5; i++)
    {
        pthread_join(pid[i], NULL);
        sem_destroy(&chopsticks[i]);
    }
    for (int i = 0; i < 2; i++)
        sem_destroy(&max[i]);

    return 0;
}
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值