C++死锁与哲学家就餐问题
“操作系统”专栏内含课设报告+实验报告+期末复习整理 C++存储管理算法设计之【内存空间的分配和回收】
设计一 采用预先分配法预防死锁的哲学家就餐问题
1. 实验目的
理解死锁的概念,掌握死锁预防方法。
死锁是进程并发执行过程中可能出现的现象,哲学家就餐问题是描述死锁的经典例子。为了防止死锁,可以采用资源预先分配法。资源预先分配法是指进程在运行前一次性地向系统申请它所需要的全部资源,如果系统当前不能够满足进程的全部资源请求,则不分配资源, 此进程暂不投入运行,如果系统当前能够满足进程的全部资源请求, 则一次性地将所申请的资源全部分配给申请进程。在哲学家就餐问题中,要采用资源预先分配法只需让每个哲学家同时申请左右两根筷子。
2. 实验要求
利用多线程技术编写哲学家就餐程序,演示采用死锁防止方法后不产生死锁的情况。
3. 实验步骤
3.1 程序结构设计
程序需要六个线程,主线程用于显示功能描述;五个哲学家线程用于模拟哲学家的活动,即不停地思考、饥饿、进食。相邻的两个哲学家线程需要共享他们中间的同一根筷子,因此对每一根筷子的使用要互斥,用互斥体数组h_mutex_chopsticks来实现。主线程创建五个哲学家线程后要等待所有哲学家结束,用线程句柄数组h_thread来表示五个线程,主线程通过等待这五个线程句柄来实现同步。
该程序共有7个函数,这些函数可以分成4组。各组包含的函数及其功能如图4-1所示。
组别 |
包括函数 |
函数功能 |
一 |
main() |
显示主菜单,接收用户的选择并执行相应的功能。 |
二 |
deadlock_philosopher() deadlock() |
演示死锁情况的哲学家线程函数 初始化函数:创建五个哲学家并等待它们结束 |
三 |
ordered_allocation_philosopher() ordered_allocation() |
通过按序分配法防止死锁的哲学家线程函数 初始化函数:创建五个哲学家并等待它们结束 |
四 |
pre_allocation_philosopher() pre_allocation() |
通过预先分配法防止死锁的哲学家线程函数 初始化函数:创建五个哲学家并等待它们结束 |
图4-1 函数及其功能 |
3.2 算法设计
下面给出预分配法函数pre _allocation_philosopher和初始化函数pre_allocation的算法描述。
设计二 采用有序分配法预防死锁的哲学家就餐问题
1. 实验目的
理解死锁的概念,掌握死锁预防方法。
死锁是进程并发执行过程中可能出现的现象,哲学家就餐问题是描述死锁的经典例子。
为了防止死锁,可以采用资源有序分配法。资源有序分配法是指事先将所有资源类全排序, 即赋予每一个资源类一个唯一的整数,规定进程必需按照资源编号由小到大的次序申请资源。
在哲学家就餐问题中,要采用资源有序分配法只需规定每个哲学家先申请左右两根筷子中编号小的筷子,再申请编号大的筷子。
2. 实验要求
利用多线程技术编写哲学家就餐程序,演示采用死锁防止方法后不产生死锁的情况。
3.2 算法设计
设计三 不预防死锁情况下的哲学家就餐问题
1. 实验目的
理解死锁的概念。
死锁是进程并发执行过程中可能出现的现象,哲学家就餐问题是描述死锁的经典例子。假设有几位哲学家围坐在一张餐桌旁,桌上有吃不尽的食品,每两位哲学家之间摆放着一根筷子,筷子的个数与哲学家的数量相等,每一位哲学家要么思考,要么等待,要么拿起左右两根筷子进餐。本设计假设有五个哲学家和五根筷子,它们的编号都是从0到4。 如果每位哲学家都拿起左边的筷子,就会发生死锁。
2. 实验要求
利用多线程技术编写哲学家就餐程序,使之在运行时能演示产生死锁的情况。
3.2 算法设计
在windows中可以用系统调用WaitForMultipleObjects()同时申请两份资源,但是在linux中没有相应的系统调用,因此要在linux下实现资源预分配法,就要自己编写同时申请两根筷子的函数。这需要将哲学家的状态增至三个, 即思考、饥俄、进食,每个哲学家仅在饥俄时才申请筷子,而且同时申请其左右两根筷子,如果此时左右两根子不同时空闲,则哲学家将等待。具体解法如下所示。
#define N 5typedef enum{thinking, hungry, eating}status;status state[N];semaphore self[N];
semaphore mutex = 1;void test(int i){
if((state[i] == hungry)&&(state[(i-1)%N] != eating)&&(state[(i+1)%N] != eating)){state[i] = eating;V(self[i]);}
}void pick_chopsticks(int i){P(mutex);state[i] = hungry;test(i);V(mutex);P(self[i]);}void put_chopsticks(int i){P(mutex);state[i] = thinking;test((i-1)%N);test((i+1)%N);V(mutex);
}void philosopher(int i){while(1){think();pick_chopsticks(i);eat();put_chopsticks(i);}void main
{int i;for(i=0;i<5;i++){
state[i] = thingking;
self[i].value = 0;}}
在上述程序中, 自定义数据类型status用来枚举哲学家的状态,数组state用来存放五个哲学家的状态,由于该数组是全局变量,所以用信号灯变量mutex实现对它的互斥访问。信号量数组self包含五个元素,每个元素的初始值皆为0,当第i号哲学家不具备进食条件时,会将自己阻塞在信号量self[i]上。函数test用于测试i号哲学家是否具备进食的条件。i号哲学家可以进食必须同时满足以下条件:i号哲学家饥饿,左边哲学家不在进食,右边哲学家不在进食。
【程序代码】
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <curses.h>
#include <time.h>
#include <semaphore.h>
#include <string.h>
#define MAX_PHILOSOPHERS 5
#define ZERO 48
#define DELAY (rand()%25)/1000typedef enum {thinking,hungry,eating}status; //枚举哲学家的状态
status state[MAX_PHILOSOPHERS]; //存放五个哲学家的状态,全局变量
pthread_mutex_t pre_mutex; //所以用信号灯变量mutex实现对state的互斥访问
sem_t pre_self[MAX_PHILOSOPHERS]; //信号量数组self包含五个元素,每个元素的初始值皆为0
pthread_mutex_t h_mutex_chopsticks[MAX_PHILOSOPHERS] //互斥体数组实现对每一根筷子的互斥使用
int thread_number[MAX_PHILOSOPHERS]={0,1,2,3,4}; void pre_test(int i);
void pre_pick_fork(int i);
void pre_put_fork(int i);//演示死锁情况的哲学家线程函数
void* deadlock_philosopher(void* data){int philosopher_number=*(int *)(data);int i=0;for(;;){srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) ); //随机等待一段时间sleep(DELAY);if(i>=5){i=0;clear();refresh();}elsei++;printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number,
" is waiting chopstick ",ZERO+philosopher_number); //提示等待左筷子refresh();pthread_mutex_lock(&h_mutex_chopsticks[philosopher_number]);sleep(DELAY/4); //随机等待一段时间printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number,
" is waiting chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);refresh();pthread_mutex_lock(
&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]
); //申请右筷子printw("%s%c%s\n","Philosopher",ZERO+philosopher_number,"is eating.");
//提示正在进餐refresh();sleep(DELAY);printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number,
" is releasing chopstick ",ZERO+philosopher_number); //放下左筷子refresh();pthread_mutex_unlock(&h_mutex_chopsticks[philosopher_number]);printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number,
"is releasing chopstick ", ZERO+(1+philosopher_number) //放下右筷子
%MAX_PHILOSOPHERS);refresh();pthread_mutex_unlock(
&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]
);sleep(DELAY);} return 0;
}//初始化函数:创建五个哲学家并等待它们结束
void deadlock(){int i=0;pthread_t h_thread[MAX_PHILOSOPHERS];printw("deadlock possible.\n");refresh();for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_mutex_init(&h_mutex_chopsticks[i],NULL);};for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_create(&h_thread[i],NULL,deadlock_philosopher,&thread_number[i]);};for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_join(h_thread[i],NULL);}
}//通过按序分配法防止死锁的哲学家线程函数
void* ordered_allocation_philosopher(void* data){int philosopher_number=*(int *)(data);int i=0;for(;;){srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) ); //随机等待一段时间sleep(DELAY);if(i>=5){i=0;clear();refresh();}elsei++;if(philosopher_number==MAX_PHILOSOPHERS-1){printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number,
" is waiting chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);refresh(); //提示等待左右两边编号较小的筷子pthread_mutex_lock(
&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);
//申请编号较小的筷子sleep(DELAY/4); //随机等待一段时间printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+philosopher_number);refresh();pthread_mutex_lock(&h_mutex_chopsticks[philosopher_number]);} //提示等待左右两边编号较大的筷子else{printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+philosopher_number);refresh();pthread_mutex_lock(&h_mutex_chopsticks[philosopher_number]);sleep(DELAY/4);printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is waiting chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);refresh(); //提示正在进餐pthread_mutex_lock(
&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]);}printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is eating.");refresh();sleep(DELAY);printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is releasing chopstick ",ZERO+philosopher_number); //放下编号较小的筷子refresh();pthread_mutex_unlock(&h_mutex_chopsticks[philosopher_number]);printw("%s%c%s%c\n","Philosopher ",ZERO+philosopher_number," is releasing chopstick ",ZERO+(1+philosopher_number)%MAX_PHILOSOPHERS);refresh(); //放下编号较大的筷子
pthread_mutex_unlock(&h_mutex_chopsticks[(1+philosopher_number)%MAX_PHILOSOPHERS]
);sleep(DELAY);} return 0;
}//初始化函数:创建五个哲学家并等待它们结束
void* ordered_allocation(){int i=0;pthread_t h_thread[MAX_PHILOSOPHERS]; //线程句柄数组h_thread来表示五个线程,主线程通过等待这五个线程句柄来实现同步printw("orderded allocation:deadlock impossible.\n");refresh();for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_mutex_init(&h_mutex_chopsticks[i],NULL);};for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_create(&h_thread[i],NULL,ordered_allocation_philosopher,&thread_number[i]);};for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_join(h_thread[i],NULL);}
}//通过预先分配法防止死锁的哲学家线程函数
void* pre_allocation_philosopher(void* data){int philosopher_number=*((int*)(data));int i=0;for(;;){srand( (unsigned)time( NULL ) * ( philosopher_number+ 1) );sleep(DELAY); //随机等待一段时间if(i>=10){i=0;clear();refresh();}elsei++;printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number,"is thinking ");refresh(); //提示等待左边筷子state[philosopher_number]=thinking;pre_pick_fork(philosopher_number);printw("%s%c%s\n","Philosopher ",ZERO+philosopher_number," is eating.");refresh(); //提示等待右筷子state[philosopher_number]=eating;sleep(DELAY);pre_put_fork(philosopher_number);sleep(DELAY);}return 0;
}void pre_pick_fork(int i){pthread_mutex_lock(&pre_mutex);state[i]=hungry;printw("%s%c%s\n","Philosopher ",ZERO+i," is hungry. ");pre_test(i);pthread_mutex_unlock(&pre_mutex);sem_wait(&pre_self[i]);
}void pre_put_fork(int i){pthread_mutex_lock(&pre_mutex);state[i]=thinking;pre_test((i-1)%MAX_PHILOSOPHERS); //放下左筷子pre_test((i+1)%MAX_PHILOSOPHERS); //放下右筷子pthread_mutex_unlock(&pre_mutex);
}
void pre_test(int i){if((state[i]==hungry)
&&(state[(i-1)%MAX_PHILOSOPHERS]!=eating)
&&(state[(i+1)%MAX_PHILOSOPHERS]!=eating)){state[i]=eating; //提示正在就餐sem_post(&pre_self[i]);}
}//初始化函数:创建五个哲学家并等待它们结束
void pre_alloction(){int i=0;pthread_t h_thread[MAX_PHILOSOPHERS];pthread_mutex_init(&pre_mutex,NULL);printw("pre_allocation:deadlock impossible.\n");refresh();for(i=0;i<MAX_PHILOSOPHERS;i++){sem_init(&pre_self[i],0,0);state[i]=thinking;};for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_create(&h_thread[i],NULL,pre_allocation_philosopher,&thread_number[i]);};for(i=0;i<MAX_PHILOSOPHERS;i++){pthread_join(h_thread[i],NULL);}pthread_mutex_destroy(&pre_mutex);
}//显示主菜单,接收用户的选择并执行相应的功能。
int main(int argc,char *argv[]){char select;bool end=false;initscr();while(!end){clear();refresh();printw("|-----------------------------------------|\n");printw("| 1:deadlock |\n");printw("| 2:non_deadlock by ordered allocation |\n");printw("| 3:non_deadlock by pre_allocation |\n");printw("| 4:exit |\n");printw("|-----------------------------------------|\n");printw("select a function(1~4):");do{select=(char)getch();}while(select!='1'&&select!='2'&&select!='3'&&select!='4');clear();refresh();switch(select){case '1':deadlock();break;case '2':ordered_allocation();break;case '3':pre_alloction();break;case '4':end=true;}printw("\nPress any key to return to main menu.");getch();clear();refresh();}endwin();return 0;
}
【实验结果】
【实验心得】
由荷兰学者Dijkstra提出的哲学家进餐问题(The Dinning Philosophers Problem)是经典的同步问题之一。哲学家进餐问题是一大类并发控制问题的典型例子,涉及信号量机制、管程机制以及死锁等操作系统中关键问题的应用,在操作系统文化史上具有非常重要的地位。对该问题的剖析有助于深刻地理解计算机系统中的资源共享、进程同步机制、死锁等问题,并能熟练地将该问题的解决思想应用于生活中的控制流程。通过本次实验,我受益匪浅。
C++死锁与哲学家就餐问题相关推荐
- 利用记录型信号量解决不会出现死锁的哲学家就餐问题
试利用记录性信号量写出一个不会出现死锁的哲学家进餐问题的算法 规定在拿到左侧的筷子后,先检查右面的筷子是否可用.如果不可用,则先放下左侧筷子, 等一段时间再重复整个过程. 分析:当出现以下情形,在某一 ...
- 多线程“死锁”之“哲学家就餐”代码实现
死锁:就是两个或者两个以上的线程相互占用对方的需要的资源,而不进行释放,导致彼此都在等待对方释放资源,产生了无限制的等待的现象. "哲学家就餐"的问题不在赘述,可以自行百度或者Go ...
- java中哲学家就餐死锁_哲学家就餐问题与死锁总结
死锁的四个条件: (1) 互斥条件:一个资源每次只能被一个进程使用. (2) 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放. (3) 不剥夺条件:进程已获得的资源,在末使用完之前 ...
- 哲学家就餐与死锁问题,死锁产生的条件以及解决方案
请结合经典案例-哲学家就餐,来谈谈你对死锁的理解,以及怎么预防和解除死锁? 哲学家就餐 描述:在一张圆桌上,有n个哲学家,n支筷子,他们的生活方式只是交替地进行思考和进餐,饥饿时便试图取其左.右最靠近 ...
- 哲学家就餐问题--信号量和互斥量预防死锁
哲学家就餐问题可以采取预防死锁的方案,就是使用互斥量和信号量锁定资源. 互斥量: 对资源进行锁定的意思就是说,当一个哲学家使用叉子的时候,他首先要先把叉子锁定,然后,拿起来.这个时候如果别的哲学家也来 ...
- 哲学家就餐(避免死锁)(多进程版)
哲学家就餐(避免死锁)(多进程版) 哲学家就餐利用信号量在多进程之间实现 下面展示一些代码片段 #include <stdio.h> #include <unistd.h> # ...
- 哲学家就餐问题(如何避免死锁)(多线程版)
哲学家就餐问题 多线程编程中,常常会遇到线程间访问共享资源的问题,如果处理不当则会发生死锁,某一个线程可能永远访问不到共享资源. 为了避免死锁的发生,提出哲学家就餐问题. 下面展示一些代码片段 #in ...
- Java多线程学习四十二:有哪些解决死锁问题的策略和哲学家就餐问题
线上发生死锁应该怎么办 如果线上环境发生了死锁,那么其实不良后果就已经造成了,修复死锁的最好时机在于"防患于未然",而不是事后补救.就好比发生火灾时,一旦着了大火,想要不造成损失去 ...
- Thinking in Java---从哲学家就餐问题看死锁现象
我们知道一个对象可以有synchronized方法或其他形式的加锁机制来防止别的线程在互斥还没释放的时候就访问这个对象.而且我们知道线程是会变成阻塞状态的(挂起),所以有时候就会发生死锁的情况:某个任 ...
- 哲学家就餐问题的三种避免死锁的解法(PV操作)
哲学家就餐问题的三种避免死锁的解法(PV操作) 方案一:最多允许有四位哲学家同时去拿左边的筷子,然后再拿右边的筷子,最终保证至少有一位哲学家能够进餐,并在就餐完毕时同时释放他用过的两只筷子,从而使更多 ...
最新文章
- Java系列 – 用Java8新特性进行Java开发太爽了(续)
- wxWidgets:wxStaticText类用法
- 【HRS项目】Axure兴许问题解决---与SVN结合
- linux 安装maven
- html5 css3浏览器,五大主流浏览器CSS3和HTML5兼容性大比拼
- Python之List和Tuple类型(入门3)
- mysql中存储过程定义
- C# 3.0语言新特性(语言规范):4 对象和集合初始化器
- matlab中如何画零线,不接零线的教训好惨 - 通信工程设计与建设 - 通信人家园 - Powered by C114...
- NOIP201103瑞士轮【B002】
- 旧电脑装什么系统最快_旧电脑装什么系统好_老旧电脑适合装什么操作系统
- 资源变现微信小程序安装教程
- Gradle使用Junit5进行test
- 十六、Swift 可选值链条 Optional Chaining
- PTC过流保护器件工作原理及选型方法
- 【Spring学习笔记】AOP
- 【在CentOS中使用Crow-基于C++的Web服务】
- Python中基本输入和输出
- 求问大神指点,tensorflow的图像分类模型进行android移植的时候出现了如下报错
- 国家氢能产业政策:氢能源股票龙头及上市公司