用Linux C的互斥锁机制解决哲学家就餐问题(简单实现)
2013-09-29 18:50
267 查看
在1971年,著名的计算机科学家艾兹格·迪科斯彻提出了一个同步问题,即假设有五台计算机都试图访问五份共享的磁带驱动器。稍后,这个问题被托尼·霍尔重新表述为哲学家就餐问题。这个问题可以用来解释死锁和资源耗尽。
「哲学家就餐说明」:有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个筷子,他们的生活方式是交替进行思考和就餐,通常,一个哲学家饥饿时就会试图去取用其左右最靠近自己的筷子,只有拿到两个筷子才能就餐,就餐完毕,放下筷子继续思考!
但其中存在这一中问题,当五个人同时饥饿的时候,每个人拿着自己左边或右边的一只筷子,就会产生「死锁」状态,当然我们可以给每个人加上时间,等待几分钟后就必须放下手中的筷子,这种情况貌似解决饿了死锁问题,但是没法避免所谓的「活锁」,在计算机中,五条线程同时进入同一状态,拿起五只筷子。所以,这种情况在计算机资源分配就会是不合理的。
在实际的计算机问题中,缺乏餐叉可以类比为缺乏共享资源。计算机技术是常用的是所谓的资源加锁,用来保证在某个时刻,资源只能被一个程序或一段代码访问。当一个程序想要使用的资源已经被另一个程序锁定,它就等待资源解锁。当多个程序涉及到加锁的资源时,在某些情况下就有可能发生死锁。例如,某个程序需要访问两个文件,当两个这样的程序各锁了一个文件,那它们都在等待对方解锁另一个文件,而这永远不会发生。
所以在这里我们用计算机中的五个条件变量来模拟五只筷子的状态,用五条线程模拟五个philosopher,他们所能进行的操作便是:
对于可能产生的死锁问题,我们这里采用一中解决的办法,那就是只有当哲学接的左右两只筷子均处于可用状态时,才允许他拿起筷子。这样就可以避免他们同时拿起筷子就餐,导致死锁。
下面是利用Linux线程互斥锁机制对这个问题的一种代码实现:
编译:
当然还有其他的策略,比如至多允许有四位哲学家同时去拿左边的筷子,最终可以保证至少有一位哲学家进餐,并在就餐完毕,释放他的筷子资源,其他的philosopher就有就会可以就餐了。
还有一中策略就是引入服务生的概念(Wiki上):哲学家必须经过他的允许才能拿起餐叉。因为服务生知道哪只餐叉正在使用,所以他能够作出判断避免死锁。为了演示这种解法,假设哲学家依次标号为A至E。如果A和C在吃东西,则有四只餐叉在使用中。B坐在A和C之间,所以两只餐叉都无法使用,而D和E之间有一只空余的餐叉。假设这时D想要吃东西。如果他拿起了第五只餐叉,就有可能发生死锁。相反,如果他征求服务生同意,服务生会让他等待。这样,我们就能保证下次当两把餐叉空余出来时,一定有一位哲学家可以成功的得到一对餐叉,从而避免了死锁。
「哲学家就餐说明」:有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个筷子,他们的生活方式是交替进行思考和就餐,通常,一个哲学家饥饿时就会试图去取用其左右最靠近自己的筷子,只有拿到两个筷子才能就餐,就餐完毕,放下筷子继续思考!
但其中存在这一中问题,当五个人同时饥饿的时候,每个人拿着自己左边或右边的一只筷子,就会产生「死锁」状态,当然我们可以给每个人加上时间,等待几分钟后就必须放下手中的筷子,这种情况貌似解决饿了死锁问题,但是没法避免所谓的「活锁」,在计算机中,五条线程同时进入同一状态,拿起五只筷子。所以,这种情况在计算机资源分配就会是不合理的。
在实际的计算机问题中,缺乏餐叉可以类比为缺乏共享资源。计算机技术是常用的是所谓的资源加锁,用来保证在某个时刻,资源只能被一个程序或一段代码访问。当一个程序想要使用的资源已经被另一个程序锁定,它就等待资源解锁。当多个程序涉及到加锁的资源时,在某些情况下就有可能发生死锁。例如,某个程序需要访问两个文件,当两个这样的程序各锁了一个文件,那它们都在等待对方解锁另一个文件,而这永远不会发生。
所以在这里我们用计算机中的五个条件变量来模拟五只筷子的状态,用五条线程模拟五个philosopher,他们所能进行的操作便是:
while(1){ thinking(); //思考 take_forks(); //拿筷子 eating(); //吃饭 put_down_forks(); } //放下筷子
对于可能产生的死锁问题,我们这里采用一中解决的办法,那就是只有当哲学接的左右两只筷子均处于可用状态时,才允许他拿起筷子。这样就可以避免他们同时拿起筷子就餐,导致死锁。
下面是利用Linux线程互斥锁机制对这个问题的一种代码实现:
#include <unistd.h> #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <pthread.h> #define phi_num 5 #define think_time 2 #define eat_time 1 #define left (phi_id+phi_num-1)%phi_num #define right (phi_id+1)%phi_num enum { think , hungry , eat } phi_state[phi_num]; pthread_mutex_t mutex=PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t state[phi_num]={PTHREAD_MUTEX_INITIALIZER}; void Do_think(int phi_id){ printf(" philosopher %d is thinking now !\n",phi_id); sleep(think_time); } void Do_eat(int phi_id){ printf("philosopher %d is eating now !\n",phi_id); sleep(eat_time); } void check_phi_state(int phi_id){ if(phi_state[phi_id]==hungry&&phi_state[left]!=eat&&phi_state[right]!=eat){ phi_state[phi_id]=eat; pthread_mutex_unlock(&state[phi_id]); } } void Do_take_forks(int phi_id){ pthread_mutex_lock(&mutex); phi_state[phi_id]=hungry; check_phi_state(phi_id); pthread_mutex_unlock(&mutex); pthread_mutex_lock(&state[phi_id]); } void Do_put_forks(int phi_id){ pthread_mutex_lock(&mutex); phi_state[phi_id]=think; check_phi_state(left); check_phi_state(right); pthread_mutex_unlock(&mutex); } void *philosopher(void *arg){ int phi_id=*(int *)arg; while(1){ Do_think(phi_id); Do_take_forks(phi_id); Do_eat(phi_id); Do_put_forks(phi_id); } return NULL; } int main(int argc, char *argv[]){ int num; pthread_t *phi=(pthread_t*)malloc(sizeof(pthread_t)*phi_num); int *id=(int *)malloc(sizeof(int)*phi_num); for(num=0;num<phi_num;num++){ id[num]=num; pthread_create(&phi[num],NULL,philosopher,(void*)(&id[num])); } for(num=0;num<phi_num;num++){ pthread_join(phi[num],NULL); } return 0; }
编译:
clang -o philosopher philosopher.c -Wall -lpthread
当然还有其他的策略,比如至多允许有四位哲学家同时去拿左边的筷子,最终可以保证至少有一位哲学家进餐,并在就餐完毕,释放他的筷子资源,其他的philosopher就有就会可以就餐了。
还有一中策略就是引入服务生的概念(Wiki上):哲学家必须经过他的允许才能拿起餐叉。因为服务生知道哪只餐叉正在使用,所以他能够作出判断避免死锁。为了演示这种解法,假设哲学家依次标号为A至E。如果A和C在吃东西,则有四只餐叉在使用中。B坐在A和C之间,所以两只餐叉都无法使用,而D和E之间有一只空余的餐叉。假设这时D想要吃东西。如果他拿起了第五只餐叉,就有可能发生死锁。相反,如果他征求服务生同意,服务生会让他等待。这样,我们就能保证下次当两把餐叉空余出来时,一定有一位哲学家可以成功的得到一对餐叉,从而避免了死锁。
相关文章推荐
- [Linux项目实践] 物联网单板测试:OLED Dis_Menu
- [Linux项目实践] 物联网单板测试之任务三:OLED菜单控制LED
- linux cpu的常用信息
- [MySQL] Linux下MySQL-5.6源码安装
- Linux安装mysql——源码安装
- linux中fork()函数详解
- Centos6.4错误的修改了rc.local无法启动系统解决方案
- linux文件操作open close read write lseek
- Linux内核系统定时器TIMER实现过程分析
- Linux 2.6 内核栈和用户栈的区别
- linux CentOS x64 里php源码编译出错参见情况及解决办法
- 源码包和二进制包区别
- Linux学习脚印
- Linux 下压缩与解压.zip和.rar及.7z文件
- selinux 设置 (13)Permission denied: access to /hsync/ denied
- linux防火墙设置相关
- linux内核链表分析
- Linux centos关机与重启命令
- linux清空文件内容而不删除文件
- linux内核--添加系统调用