利用Linux下的pthread_mutex_t类型来实现哲学家进餐问题
首先说一下什么是哲学家进餐问题,这是操作系统课程中一个经典的同步问题,

问题如下:如上图,有6个哲学家和6根筷子(那个蓝色部分表示哲学家,那个紫色长条部分表示筷子),他们分别被编了0~5的号!如果某个哲学家想要进餐的话,必须同时拿起左手和右手边的两根筷子才能进餐!哲学家进餐完毕之后,就放下手中拿起的两根筷子!这样其他哲学家就能拿这些筷子进餐了!
OK,这样就可能存在一个死锁问题,比如0号哲学家拿了0号筷子,1号哲学家拿了1号筷子!如此往复,最终的结果就是每个哲学家都只拿了1根筷子,每个人都无法进餐,同时也无法放下手中的筷子!这样就产生了死锁!
那么死锁该如何解决呢?很简单,就是根据哲学家的编号!如果是偶数号的哲学家,则先拿右手边筷子(小的号),再拿左手边的筷子(大的号)。而奇数号的哲学家则刚好相反!这样,就不会出现每个哲学家都只拿了一根筷子的情况出现了!
具体程序如何实现呢?代码如下:
/* 说明,本程序是为了模拟实现哲学家进餐的问题,一共有6个哲学家和6根筷子
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<errno.h>
#include<pthread.h> #define B_SIZE 4096
#define NUM_P 6 typedef struct phi
{
pthread_mutex_t chopsticks[NUM_P]; //五根筷子
int num; //哲学家的编号
pthread_mutex_t num_lock; //哲学家编号的锁
}Phi,*PPhi;
void * tfunc(void *arg)
{
PPhi sp = (PPhi)arg;
int phi_num;
int next_num; /*读取哲学家的编号*/
phi_num = sp->num;
pthread_mutex_unlock(&sp->num_lock);
printf("No.%d philosopher has comed\n",phi_num);
/*下一根筷子*/
next_num= phi_num+>=NUM_P?phi_num+-NUM_P:phi_num+; sleep(); //所有线程统一睡眠5S,来等待其他线程创建完成 if(phi_num% == ) //奇数号先拿大的,再拿小的
{
pthread_mutex_lock(&(sp->chopsticks[next_num]));
//printf("No.%d philosopher lock the No.%d chopstick\n",phi_num,next_num);
pthread_mutex_lock(&(sp->chopsticks[phi_num]));
//printf("No.%d philosopher lock the No.%d chopstick\n",phi_num,phi_num); printf("No.%d philosopher has eated!\n",phi_num); pthread_mutex_unlock(&(sp->chopsticks[next_num]));
//printf("No.%d philosopher unlock the No.%d chopstick\n",phi_num,next_num);
pthread_mutex_unlock(&(sp->chopsticks[phi_num]));
//printf("No.%d philosopher unlock the No.%d chopstick\n",phi_num,phi_num);
}
else //偶数号先拿小的,再拿大的
{
pthread_mutex_lock(&(sp->chopsticks[phi_num]));
//printf("No.%d philosopher lock the No.%d chopstick\n",phi_num,phi_num);
pthread_mutex_lock(&(sp->chopsticks[next_num]));
//printf("No.%d philosopher lock the No.%d chopstick\n",phi_num,next_num); printf("No.%d philosopher has eated!\n",phi_num); pthread_mutex_unlock(&(sp->chopsticks[phi_num]));
//printf("No.%d philosopher unlock the No.%d chopstick\n",phi_num,phi_num);
pthread_mutex_unlock(&(sp->chopsticks[next_num]));
//printf("No.%d philosopher unlock the No.%d chopstick\n",phi_num,next_num);
} return (void*);
}
int main(int argc,char *argv[])
{
int err;
pthread_t tid[NUM_P];
char buf_err[B_SIZE]; //用于保存错误信息
void *retv; //子线程的返回值
Phi phis; for(int loop=;loop<NUM_P;loop++)
{
/*设置哲学家的编号*/
pthread_mutex_lock(&phis.num_lock);
phis.num = loop; /*创建子线程当作哲学家*/
err = pthread_create(&tid[loop],NULL,tfunc,&phis);
if( != err)
{
memset(buf_err,,B_SIZE);
sprintf(buf_err,"[create]:%s\n",strerror(err));
fputs(buf_err,stderr);
exit(EXIT_FAILURE);
}
} for(int loop=;loop<NUM_P;loop++)
{
err = pthread_join(tid[loop],&retv);
if( != err)
{
memset(buf_err,,B_SIZE);
sprintf(buf_err,"[join]:%s\n",strerror(err));
fputs(buf_err,stderr);
exit(EXIT_FAILURE);
} printf("Main:thread return the value: %d\n",(int)retv);
} return ;
}
在上面的程序中,我创建了一个结构体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,就是这些了!希望能够对学习操作系统的朋友们产生一些帮助!
利用Linux下的pthread_mutex_t类型来实现哲学家进餐问题的更多相关文章
- linux下的文件类型
在Linux中一切设备皆文件,首先来看一下Linux下的文件都有哪些分类,也就是文件类型 文件类型:普通文件(包括shell脚本,文档,音频,视频).目录文件.设备文件(又细分为字符设备文件和块设备文 ...
- QT在linux下获取网络类型
开发中遇到这样一个需求,需要判断当前网络的类型(wifi或者4G或者网线),在这里给大家一块分享下: 1.这里有一个linux指令:nmcli(大家自行百度即可) 2.nmcli device sta ...
- linux下查看文件系统类型
1. df -hT命令 -h, --human-readable print sizes in human readable format (e.g., 1K 234M 2G) -T, --pr ...
- [ 原创 ] Linux下查找指定类型文件以及删除
find ./ -name "*.lok" // 查找文件find ./ -name "*.lok" |xargs rm -fr // 查找文件并删除
- linux下创建库函数
来源: 在Linux下如何使用自己的库函数-riverok-ChinaUnix博客 http://blog.chinaunix.net/uid-21393885-id-88128.html 构建Lin ...
- 【驱动】linux下I2C驱动架构全面分析
I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一条串行数据线:SDA,一条是时钟线SCL ,使用SCL,SDA这两根信号线就实现了设备之间的数据交互,它方便了工程师的布线. ...
- linux下I2C驱动架构全面分析【转】
本文转载自:http://blog.csdn.net/wangpengqi/article/details/17711165 I2C 概述 I2C是philips提出的外设总线. I2C只有两条线,一 ...
- Linux下的文件及文件后缀名
Linux下的文件及文件后缀名 2013-03-14 15:34 6969人阅读 评论(0) 收藏 举报 ++++++++++++++++++++++++++++++++++++++正文+++++++ ...
- linux下动态链接库.so文件 静态链接库.a文件创建及使用
转摘网址为:http://www.cnblogs.com/fengyv/archive/2012/08/10/2631313.html Linux下文件的类型是不依赖于其后缀名的,但一般来讲: ...
随机推荐
- cocos2d粒子效果
第9章 粒子效果 游戏开发者通常使用粒子系统来制作视觉特效.粒子系统能够发射大量细小的粒子并对他们进行渲染,而且效率要远高于渲染同样数目的精灵.粒子系统可以模拟下雨.火焰.雪.爆炸.蒸气拖尾以及其他多 ...
- springMVC1 springmvc的基础知识
springmvc第一天 springmvc的基础知识 springmvc课程安排: 第一天: 基础知识 springmvc框架(重点) mvc在b/s系统中应用方式 springmvc框架原理(Di ...
- [Effective C++ --032]确定你的public继承塑模出is-a
这一章都在讲述继承的关系.可以举个例子说明: 父类是水果,子类是苹果,苹果是一种(is-a)水果,但是水果不一定就是苹果. is-a并不是唯一存在classes之间的关系.另两个常见的关系是has-a ...
- 实例源码--Android小工具源码
下载源码 技术要点: 1. Android控件布局的使用 2. Http通信 3. XML数据解析 4. 网络状态的监听 5. 源码带有非常详细的中文注释 ...... 详细介绍: 1. An ...
- js自动提交按钮
document.forms['alipaysubmit'].submit(); <form id='alipaysubmit' name='alipaysubmit' action='' me ...
- JavaWeb中登陆功能
首先我们要JavaWeb登陆的基本流程:JSP页面发送请求-->Servlet-->Servlet通过调用方法从数据库中得到数据并将结果返回页面 我们先建立三个jsp页面,包括login. ...
- hdu 1394 树状数组
思路:从后面往前面统计,每次先sum+=Sum(num[i]+1),然后在update(num[i]+1,1).这样每次Sum每次加的个数就是num[i]的逆序对个数. 每次从队首调一个元素到队尾,逆 ...
- 查锁表及kill
当一个表一直被锁住而无法进行操作的时候,可以用如下方法 select l.session_id sid, s.serial#, l.locked_mode 锁模式, l.oracle_username ...
- Codevs 2307[SDOI2009]HH的项链
同题: Codevs 2307 HH的项链 BZOJ 1878 HH的项链 洛谷 1972 HH的项链 2009年省队选拔赛山东 时间限制: 1 s 空间限 ...
- Asp.Net MVC 路由 【转】
原文链接:http://www.asp.net/learn/mvc/ 在这篇教程中,我将为你介绍每个ASP.NET MVC应用程序都具有的一个重要功能,称作ASP.NET路由(ASP.NET Rout ...