• 利用Linux下的pthread_mutex_t类型来实现哲学家进餐问题


      首先说一下什么是哲学家进餐问题,这是操作系统课程中一个经典的同步问题,

      

      问题如下:如上图,有6个哲学家和6根筷子(那个蓝色部分表示哲学家,那个紫色长条部分表示筷子),他们分别被编了0~5的号!如果某个哲学家想要进餐的话,必须同时拿起左手和右手边的两根筷子才能进餐!哲学家进餐完毕之后,就放下手中拿起的两根筷子!这样其他哲学家就能拿这些筷子进餐了!

      OK,这样就可能存在一个死锁问题,比如0号哲学家拿了0号筷子,1号哲学家拿了1号筷子!如此往复,最终的结果就是每个哲学家都只拿了1根筷子,每个人都无法进餐,同时也无法放下手中的筷子!这样就产生了死锁!

      那么死锁该如何解决呢?很简单,就是根据哲学家的编号!如果是偶数号的哲学家,则先拿右手边筷子(小的号),再拿左手边的筷子(大的号)。而奇数号的哲学家则刚好相反!这样,就不会出现每个哲学家都只拿了一根筷子的情况出现了!

      具体程序如何实现呢?代码如下:

      1 /* 说明,本程序是为了模拟实现哲学家进餐的问题,一共有6个哲学家和6根筷子
      2  */
      3 #include<stdio.h>
      4 #include<string.h>
      5 #include<stdlib.h>
      6 #include<unistd.h>
      7 #include<errno.h>
      8 #include<pthread.h>
      9 
     10 #define B_SIZE 4096
     11 #define NUM_P 6
     12 
     13 typedef struct phi
     14 {
     15    pthread_mutex_t chopsticks[NUM_P];    //五根筷子 
     16    int num;                //哲学家的编号
     17    pthread_mutex_t num_lock;        //哲学家编号的锁
     18 }Phi,*PPhi;
     19 void * tfunc(void *arg)
     20 {
     21     PPhi sp = (PPhi)arg;
     22     int phi_num;
     23     int next_num;
     24 
     25     /*读取哲学家的编号*/
     26     phi_num = sp->num;
     27     pthread_mutex_unlock(&sp->num_lock);
     28     printf("No.%d philosopher has comed
    ",phi_num);
     29     /*下一根筷子*/
     30     next_num= phi_num+1>=NUM_P?phi_num+1-NUM_P:phi_num+1;
     31 
     32     sleep(5);    //所有线程统一睡眠5S,来等待其他线程创建完成
     33 
     34     if(phi_num%2 == 1)    //奇数号先拿大的,再拿小的
     35     {
     36     pthread_mutex_lock(&(sp->chopsticks[next_num]));
     37     //printf("No.%d philosopher lock the No.%d chopstick
    ",phi_num,next_num);
     38     pthread_mutex_lock(&(sp->chopsticks[phi_num]));
     39     //printf("No.%d philosopher lock the No.%d chopstick
    ",phi_num,phi_num);
     40 
     41     printf("No.%d philosopher has eated!
    ",phi_num);
     42 
     43     pthread_mutex_unlock(&(sp->chopsticks[next_num]));
     44     //printf("No.%d philosopher unlock the No.%d chopstick
    ",phi_num,next_num);
     45     pthread_mutex_unlock(&(sp->chopsticks[phi_num]));
     46     //printf("No.%d philosopher unlock the No.%d chopstick
    ",phi_num,phi_num);
     47     }
     48     else        //偶数号先拿小的,再拿大的
     49     {
     50     pthread_mutex_lock(&(sp->chopsticks[phi_num]));
     51     //printf("No.%d philosopher lock the No.%d chopstick
    ",phi_num,phi_num);
     52     pthread_mutex_lock(&(sp->chopsticks[next_num]));
     53     //printf("No.%d philosopher lock the No.%d chopstick
    ",phi_num,next_num);
     54 
     55     printf("No.%d philosopher has eated!
    ",phi_num);
     56 
     57     pthread_mutex_unlock(&(sp->chopsticks[phi_num]));
     58     //printf("No.%d philosopher unlock the No.%d chopstick
    ",phi_num,phi_num);
     59     pthread_mutex_unlock(&(sp->chopsticks[next_num]));
     60     //printf("No.%d philosopher unlock the No.%d chopstick
    ",phi_num,next_num);
     61     }
     62 
     63     return (void*)0;
     64 }
     65 int main(int argc,char *argv[])
     66 {
     67     int err;
     68     pthread_t tid[NUM_P];
     69     char buf_err[B_SIZE];   //用于保存错误信息
     70     void *retv;            //子线程的返回值
     71     Phi phis;
     72 
     73     for(int loop=0;loop<NUM_P;loop++)
     74     {
     75     /*设置哲学家的编号*/
     76     pthread_mutex_lock(&phis.num_lock);
     77     phis.num = loop;    
     78 
     79     /*创建子线程当作哲学家*/
     80     err = pthread_create(&tid[loop],NULL,tfunc,&phis);
     81     if(0 != err)
     82     {
     83         memset(buf_err,0,B_SIZE);
     84         sprintf(buf_err,"[create]:%s
    ",strerror(err));
     85         fputs(buf_err,stderr);
     86         exit(EXIT_FAILURE);
     87     }
     88     }
     89 
     90     for(int loop=0;loop<NUM_P;loop++)
     91     {
     92     err = pthread_join(tid[loop],&retv);
     93     if(0 != err)
     94     {
     95         memset(buf_err,0,B_SIZE);
     96         sprintf(buf_err,"[join]:%s
    ",strerror(err));
     97         fputs(buf_err,stderr);
     98         exit(EXIT_FAILURE);
     99     }
    100     
    101     printf("Main:thread return the value: %d
    ",(int)retv);
    102     }
    103 
    104     return 0;
    105 }

      在上面的程序中,我创建了一个结构体PHI!其中,chopsticks是定义的五个互斥锁,用来表示5根筷子!num变量用来表示哲学家的编号,而那个num_lock表示对哲学家编号进行一个锁定,这个为什么要这么设置,随后会讲到!

      首先讲主程序,就是创建NUM_P个子线程用来表示这么多个哲学家!在子线程里面,需要根据哲学家编号的奇偶性来选择不同的拿筷子的方法!这就需要传一个哲学家编号给这个子线程!通过什么传呢,就是PHI结构体中的num变量!这里我们会碰到一个问题!就是如果在主线程里面设置了num,随后就创建了子线程,但是子线程还没来得及读出这个num的值,主线程已经开始了下一次的循环,那么很有可能导致子线程读到的num值并不是我们想要让它读到的!所以这里就用到了num_lock这个锁,主线程写了num的值后,就把num_lock这个锁给锁定,然后子线程读到num的之后,才把num_lock这个锁给解锁,然后主线程才能进行下一次循环!这样就可以确保子线程读到的编号就是我们想要给他传的编号!

      在子线程里面,我首先输出一句"No.%d philosopher has comed ",表示某个子线程已经创建好了!然后就让这个线程睡眠5S,等待其他子线程创建完毕(这个其实不需要的,但是我感觉得让哲学家们大致是一起开始吃饭的么。。。^_~)!然后就照着上面那个思路,偶数号的哲学家先拿右手边筷子,再拿左手边筷子,奇数号哲学家则正好相反!这样就能避免死锁了!

      还有一个问题就是我在子线程里面定义的这个next_num这个变量,这个用来表示哲学家拿的下一根筷子的值,因为最大编号的那个哲学家的下一根筷子是第0号筷子,所以我这里用了一个if判断来分辨!

      最后程序的运行结果如下:

      

      OK,就是这些了!希望能够对学习操作系统的朋友们产生一些帮助!

  • 相关阅读:
    NOIP 2012 文化之旅
    史上最全的各种C++ STL容器全解析
    详解C++ STL map 容器
    详解C++ STL priority_queue 容器
    浅谈C++ STL stack 容器
    浅谈C++ STL queue 容器
    浅谈C++ STL vector 容器
    CF1185F Two Pizzas
    浅谈C++ STL deque 容器
    详解C++ STL multiset 容器
  • 原文地址:https://www.cnblogs.com/bwangel23/p/4154521.html
Copyright © 2020-2023  润新知