美文网首页
No43.进程间的同步与互斥---哲学家问题

No43.进程间的同步与互斥---哲学家问题

作者: 赫尔特 | 来源:发表于2019-12-22 14:49 被阅读0次

    文章目录

    \color{orange}{1.水果问题}
    \color{red}{2.公交车问题}
    \color{green}{3.哲学家问题}
    \color{pink}{4.读者写者问题}
    \color{purple}{5.总结}

    1.水果问题

    • 桌上有一只盘子,每次只能放一个水果,而且盘子最多装一个水果,爸爸只往里面放苹果,妈妈只往里面放香蕉,儿子只吃香蕉,女儿只吃苹果,用P、V操作实现四人正确活动的程序。
    semaphore empty=1;  //判断盘子是否为空的信号量
    semaphore apple=0,banana=0;   //是否有苹果,香蕉的信号量
    
    Father:
        P(empty)
        向盘子里放苹果;
        V(apple);
    Mother:
        P(empty)
        向盘子里放香蕉;
        V(banana);
    Daughter:
        P(apple)
        取盘子里的苹果;
        V(empty);
    Son:
        P(banana)
        取盘子里的香蕉;
        V(empty);
    
    • 桌上有一只盘子,每次只能放和取出一个水果,而且盘子最多装2个水果,爸爸只往里面放苹果,妈妈只往里面放香蕉,两个儿子只吃香蕉,两个女儿只吃苹果,用P、V操作实现六人正确活动的程序。

    用一个信号量对盘子里的水果个数进行计数,用另一个信号量用来控制对盘子的互斥访问(因为每次只能放和取出一个水果),还有苹果和香蕉的信号量

    semaphore empty=2;  //对盘子里的水果个数进行计数
    semaphore mutex=1;  //控制对盘子的互斥访问
    semaphore apple=0,banana=0;   //是否有苹果,香蕉的信号量
    
    Father:
        P(empty);
        P(mutex);
        向盘子里放苹果;
        V(mutex);
        v(apple);
    Mother:
        P(empty);
        P(mutex);
        向盘子里放香蕉;
        V(mutex);
        v(banana);
    Daughter i(i=1,2):
        P(apple);
        P(mutex);
        取盘子里的苹果;
        V(mutex);
        v(empty);
    Son i(i=1,2):
        P(banana);
        P(mutex);
        取盘子里的香蕉;
        V(mutex);
        v(empty);
    

    2.公交车问题

    • 设公交车上有一位司机和一位售票员,它们的活动如下:
    graph LR
    A[司机] --> B[启动车辆]
    B --> C[正常行驶]
    C--> D[到站停车]
    
    E[售票员] --> F[关车门]
    F --> G[售票]
    G--> H[开车门]
    

    规定:只有当售票员关车门后司机才能启动车辆,只有当司机停车门后售票员才能开车门。用P、V操作实现两人正确活动的程序。

    semaphore start=0;    //可以开车的信号量
    semaphore open=0;     //可以开门的信号量
    
    司机:
    P(start);
    启动车辆;
    正常行驶;
    到站停车;
    V(open);
    
    售票员:
    关车门;
    V(start);   //因为只有关车门后才能启动车辆
    售票;
    P(open);
    开车门;
    

    3.哲学家问题

    • 哲学家问题:
      五个哲学家围在一个圆桌旁边吃饭,每相邻两个哲学家之间有且只有1个叉子可以使用。规定:哲学家都是很讲究的,必须抓起自己旁边的两个叉子才会吃饭,否则就算饿死也不吃,哲学家不吃的时候都在思考哲学问题(我是谁?我和动物的区别是什么?我和机器的区别是什么.......),当然,哲学家也不能抓着叉子就不放,吃饭的时间是有限的。怎样才能让我们的哲学家都能吃到饭,比如五个人同时抓起一个叉子,那么五个人都没得吃。

    下面这个代码就会产生五个人都没得吃的情况:

    take_fork()和put_fork()函数内部都有P,V操作,也就是说同一个叉子不能被两个人同时拿起

    #define N 5
    
    void philosopher(int i)   //第i个哲学家
    {
    while(true){
    think();    //我要思考问题!
    take_fork();    //准备要吃饭了,拿起第i个叉子,
    //假设哲学家被编好号,这个i就是哲学家的编号,
    //它对应的是抓起左边的叉子
    take_fork((i+1)%N);    //抓起右边的叉子
    eat();    //我开动了
    put_fork(i);    //放下左边的叉子
    put_fork((i+1)%N);  //放下右边的叉子
    }
    }
    

    解决办法有很多,比如下面四种都可行:
    1.同一时间只允许四个哲学家同时拿起叉子
    2.规定拿起第一个叉子时,编号为奇数的哲学家先拿起自己左边的叉子,而编号为偶数的哲学家先拿起自己右边的叉子
    3.更暴力一些,把上面代码think()后面的五句保护起来,设置mutex,规定一次只允许一个哲学家吃饭。
    4.规定:当且仅当旁边的两个叉子都可以用时,才允许哲学家拿叉子,否则就一直等待。

    下面代码是对第4种方法的实现:

    #define N 5     //哲学家的数量
    #define LEFT (i+N-1)%N    //哲学家i的左边哲学家编号
    #define RIGHT (i+1)%N     //哲学家i的右边哲学家编号
    //下面三个宏都用来表示哲学家的状态
    #define THINKING 0    //思考中的哲学家
    #define HUNGRY 1      //饿肚子的哲学家
    #define EATING 2      //进食中的哲学家
    typedef int semaphore;
    int state[N];    //记录哲学家现在的状态,这个状态就是前面的三个宏
    semaphore mutex=1;
    semaphore s[N];    //为每个哲学家设置一个信号量
    
    void philosopher(int i)    //i是哲学家编号
    {
        while(true)
        {
            think();
            take_forks(i);
            eat();
            put_forks(i);
        }
    }
    
    void take_forks(int i)
    {
    down(&mutex);
    state[i]=HUNGRY;
    test(i);     //尝试一下能不能同时拿起左右两只叉子
    up(&mutex);
    down(&s[i]);    //如果在之前的test里不能拿起两个叉子,那么会在这条语句被锁住
    }
    
    void put_forks(int i)
    {
    down(&mutex);
    state[i]=THINKING;
    test(LEFT);    //看看左边的哲学家有没有食欲
    test(RIGHT);   //看看右边的哲学家有没有食欲
    up(&mutex);
    }
    
    void test(int i)
    {
        if(state[i[==HUNGRY && state[LEFT]!=EATING && state[RIGHT]!=EATING)
        {
            state[i]=EATING;
            up(&s[i]);
        }
    }
    

    4.读者写者问题

    • 读者和写者问题

    读者和写者都可以访问一个数据库,读者只能读,写者只能写,规定如下:
    1.允许任意多个读者同时读数据
    2.同一时间只允许一个写者修改数据
    3.写者和读者不能同时访问数据

    用P、V操作实现的思路:
    (把这个数据库想象成图书馆,方便陈述理解)
    1.偏向读者型:
    (1)当读者到来时,只要有读者在读数据,或者没有人访问数据,这个新的读者都可以读数据
    (2)当写者到来时,只有没人访问数据时才能对数据进行写

    2.偏向写者型:
    (1)当读者到来时,如果前面有写者在等待进入图书馆,读者就不能进去,当然里面有写者也不能进入
    (2)当写者到来时,里面有读者依然不能进入,只有里面没人时才能进。

    typedef int semaphore;
    semaphore mutex=1;    //控制对读者的数量进行加减的信号量
    semaphore db=1;       //图书馆的信号量,database
    int rc=0;             //图书馆里读者的数量
    
    void reader()
    {
        while(true)
        {
        //进入
            down(&mutex);
            rc++;
            if(rc==1)
            down(&db);
            up(&mutex);
            
            read_db();   //读取数据
    
        //离开
            down(&mutex);
            rc--;
            if(rc==0)
            up(&db);
            up(&mutex);
        }
    }
    
    void writer()
    {
        while(true)
        {
            down(&db);
            write_db();
            up(&db);
        }
    }
    

    5.总结

    • P、V操作必须成对出现
    • 当为互斥操作时,它们同处于同一进程。
      当为同步操作时,它们不在同一进程中。
    • 一个同步P操作与一个互斥P操作在一起时同步P操作在互斥P操作前。
    • 两个V操作的顺序无关紧要。

    相关文章

      网友评论

          本文标题:No43.进程间的同步与互斥---哲学家问题

          本文链接:https://www.haomeiwen.com/subject/cqyonctx.html