利用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\n",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\n",phi_num,next_num);
 38     pthread_mutex_lock(&(sp->chopsticks[phi_num]));
 39     //printf("No.%d philosopher lock the No.%d chopstick\n",phi_num,phi_num);
 40
 41     printf("No.%d philosopher has eated!\n",phi_num);
 42
 43     pthread_mutex_unlock(&(sp->chopsticks[next_num]));
 44     //printf("No.%d philosopher unlock the No.%d chopstick\n",phi_num,next_num);
 45     pthread_mutex_unlock(&(sp->chopsticks[phi_num]));
 46     //printf("No.%d philosopher unlock the No.%d chopstick\n",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\n",phi_num,phi_num);
 52     pthread_mutex_lock(&(sp->chopsticks[next_num]));
 53     //printf("No.%d philosopher lock the No.%d chopstick\n",phi_num,next_num);
 54
 55     printf("No.%d philosopher has eated!\n",phi_num);
 56
 57     pthread_mutex_unlock(&(sp->chopsticks[phi_num]));
 58     //printf("No.%d philosopher unlock the No.%d chopstick\n",phi_num,phi_num);
 59     pthread_mutex_unlock(&(sp->chopsticks[next_num]));
 60     //printf("No.%d philosopher unlock the No.%d chopstick\n",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\n",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\n",strerror(err));
 97         fputs(buf_err,stderr);
 98         exit(EXIT_FAILURE);
 99     }
100
101     printf("Main:thread return the value: %d\n",(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\n",表示某个子线程已经创建好了!然后就让这个线程睡眠5S,等待其他子线程创建完毕(这个其实不需要的,但是我感觉得让哲学家们大致是一起开始吃饭的么。。。^_~)!然后就照着上面那个思路,偶数号的哲学家先拿右手边筷子,再拿左手边筷子,奇数号哲学家则正好相反!这样就能避免死锁了!

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

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

  

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

时间: 2024-10-29 10:46:14

利用Linux下的pthread_mutex_t类型来实现哲学家进餐问题的相关文章

完美利用Linux下的curl来访问网页

首先,你得学会curl下的各个参数的作用,能很大程度上帮助你完成,往往需要几十行甚至几百行的代码,确只需要两三行的命令代码就能搞定一切了! -a/--append 上传文件时,附加到目标文件  -A/--user-agent <string>  模拟浏览器登陆服务器  - anyauth   可以使用"任何"身份验证方法  -b/--cookie <name=string/file> cookie字符串或文件读取位置  - basic 使用HTTP基本验证  -

linux下查看文件系统类型

1. df -hT命令   -h, --human-readable  print sizes in human readable format (e.g., 1K 234M 2G) -T, --print-type      print file system type [[email protected] ~]# df -hT Filesystem Type Size Used Avail Use% Mounted on /dev/mapper/rhel-root xfs 18G 4.5G

老男孩教育每日一题-第109天-linux下创建各种类型的文件

参考答案: 创建普通文件 touch filename 创建目录文件 mkdir dirname 创建链接文件 ln -s filename linkname 创建块设备文件 mknod /dev/sdb b 16 8 创建字符类型文件 mknod /dev/ccc c 20 5 创建socket文件 mksock a.sock 创建管道文件 mkfifo pipe 备注 今天是每日一题陪伴大家的第109天,期待你的进步. 对于题目和答案的任何疑问,请在博客评论区留言.往期题目索引

Linux下的文件类型

文件类型 属性标识 正规文件(文本文件,二进制文件,数据格式文件) - 目录文件 d 连接文件 l 设备文件(块设备,字符设备) b.c 数据接口文件(sockets) s 数据传输文件(FIFO,pipe) p

linux下创建库函数

来源: 在Linux下如何使用自己的库函数-riverok-ChinaUnix博客 http://blog.chinaunix.net/uid-21393885-id-88128.html 构建Linux下的函数库编译方案 - observer的日志 - 网易博客 http://blog.163.com/[email protected]/blog/static/23180765201111622915148/ linux下的函数库 程序 编译_ablab_新浪博客 http://blog.si

Linux下C编程-----IO/文件操作/内存映射 实现简单记录存储(3)

利用linux下的文件内存映射可以实现进程共享数据,我们可以把一个文件映射到虚拟内存中使多个进程进行共享, 到这里我们大概能想到他能应用到的领域 是很广泛的 主要涉及到 mmap  munmap   msync 三个函数的应用 下面贴代码 下面一段代码是为文件建立一个简单的记录存储,并且通过内存映射修改文件内容 /************************************************************************* > File Name: memdb

socket在windows下和linux下的区别

windows到Linux代码移植遇到的问题 1.一些常用函数的移植 http://www.vckbase.com/document/viewdoc/?id=1586 2.网络 ------ 转载 & 修改(待整理) socket相关程序从windows移植到linux下需要注意的 1)头文件 windows下winsock.h/winsock2.h linux下sys/socket.h 错误处理:errno.h 2)初始化 windows下需要用WSAStartup linux下不需要 3)关

【原创】Linux下获取命令的帮助与常用命令

Linux中的shell命令一般是执行步骤:用户在终端输入命令回车,系统内核会在当前用户的环境变量PATH中去读取环境变量的值 变量的值就是命令的路径,命令路径不只一个,于是系统会从这些路径中从左至右的顺序匹配要查找的命令文件,直到找到并执行为止:当这个命令被执行过已后,系统会把这个命令对应的文件路径保存在系统的哈希表中,以便下次用户执行命令时更快速的查找,查看当前用户的 hash 表.可以执行系统内置命令 hash 显示当前用户的哈希表.我们用which获取命令所在的路径,用type判断命令是

linux 下 float 和 double 精度计算差别

今天在根据需求写代码时候,偶尔发现linux 下 设置变量类型 float 和double 计算时, 得到的结果是不一样的. 要求: 设定值 = 传入值 * 10 * 122.88 /1000; case: 设定值 = 1666*10*122.88/1000 = 2047.1808 设置成 float时,代码: #include<stdio.h> #include <math.h> unsigned int fun(unsigned int sfn_threshold) { flo