哲学家问题
2019.12.26
一:问题描述
假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东⻄的时 候,他们就停止思考,思考的时候也停止吃东⻄。餐桌中间有一大碗意大利面,每两个哲学家之间 有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东⻄。他们只 能使用自己左右手边的那两只餐叉。 每个哲学家分为3个状态{thinking,trying,eating},分别代表思考,尝试获得叉子,吃面三个状 态。 哲学家从来不交谈,因此可能产生死锁,即某个时刻每个哲学家都拿着一只餐叉,永远都在等另一 只餐叉,也就是都处于trying的状态。
二:设计概要
解决本问题应采用多线程实现。
哲学家的刀叉由于是共享变量,所以需要作为临界区用互斥变量、信号量等机制实现互斥。本次采 用信号量实现互斥。也就需要为每个叉子定义相应的信号量,对于每一个线程,在访问共享变量叉 子的时候应先进行sem_wait,然后访问到两个叉子后,哲学家就能eating,而后进行sem_post解锁 这两个叉子。
三:避免死锁的策略
method1: 限制同一时刻最多只能有thread_count - 1个人进入餐厅尝试获取叉子,剩下的一位只有 当有人吃好出来的时候再进去,因此能够保证至少有一个人能够获得两个叉子,从而eating,这样的 话永远不会导致所有人都在trying,即能够避免死锁。 实现方法为:增加一个信号量,设置其初值为thread_count - 1,在每个线程任务开始前先 sem_wait,任务结束后sem_post。
Method2: 分奇偶数编号进餐 奇数号哲学家先取左手的叉子,然后取右手的叉子,而偶数哲学家先取右手的叉子,然后再取左手 的叉子。 实现方法为:判断当前线程的编号,如果是奇数号线程的话就先sem_wait左边的叉子,右边类似; 如果是偶数线程的话就先sem_wait右边的叉子,左边类似。
四:实验结果(全部成功)
- normal
- method1
- method2
五:分析
略
六:原代码
1. pthread版本
/*
pthread
无先生_ 2019.12.26
命令以以下形式:./deadlock -normal 5
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <pthread.h>
long thread_count; //线程个数,即哲学家个数
void *Thread_normal(void *rank); //死锁状态
void *Thread_method1(void *rank); //解锁方法1
void *Thread_method2(void *rank); //解锁方法2
sem_t *sem_fork; //定义一组叉子的信号量
sem_t sem_waiter; //method1,用于确保至少一个人不能进餐
int main(int argc, char *argv[])
{
long thread;
pthread_t *thread_handles;
thread_count = strtol(argv[2], NULL, 10); //获取线程(哲学家)个数
thread_handles = malloc(thread_count * sizeof(pthread_t));
sem_fork = malloc (thread_count * sizeof(sem_t));
sem_init(&sem_waiter, 0, 4); //初始化waiter信号量
long i;
for(i = 0; i < thread_count; i ++ ){
sem_init(&sem_fork[i], 0, 1); //初始化所有叉子信号量
}
for(thread = 0; thread < thread_count; thread ++ ){ //创建线程
if(strcmp(argv[1], "-normal") == 0 || strcmp(argv[1], "normal") == 0) //进入相应的模式
pthread_create(&thread_handles[thread], NULL, Thread_normal, (void *)thread);
else if(strcmp(argv[1], "-method1") == 0 || strcmp(argv[1], "method1") == 0)
pthread_create(&thread_handles[thread], NULL, Thread_method1, (void *)thread);
else if(strcmp(argv[1], "-method2") == 0 || strcmp(argv[1], "method2") == 0)
pthread_create(&thread_handles[thread], NULL, Thread_method2, (void *)thread);
}
for(thread = 0; thread < thread_count; thread ++ ) //join所有线程
pthread_join(thread_handles[thread], NULL);
for(i = 0; i < thread_count; i ++ ){ //删除所有叉子信号量
sem_destroy(&sem_fork[i]);
}
sem_destroy(&sem_waiter); //删除waiter信号量
free(thread_handles);
free(sem_fork);
return 0;
}
//陷入死锁
void *Thread_normal(void *rank)
{
long my_rank = (long)rank;
long my_left, my_right; //表示每个哲学家左右的叉子
my_left = my_rank;
my_right = (my_rank + 1) % thread_count;
while(1){
printf("Philosopher %ld is thinking\n", my_rank);
usleep(50); //think 50 ms
printf("Philosopher %ld is trying\n", my_rank);
sem_wait(&sem_fork[my_left]);
sem_wait(&sem_fork[my_right]);
printf("Philosopher %ld is eating\n", my_rank);
usleep(100); //eating 100 ms
sem_post(&sem_fork[my_left]);
sem_post(&sem_fork[my_right]);
}
return NULL;
}
//解锁方法1
void *Thread_method1(void *rank)
{
long my_rank = (long)rank;
long my_left, my_right; //表示每个哲学家左右的叉子
my_left = my_rank;
my_right = (my_rank + 1) % thread_count;
sem_wait(&sem_waiter); //添加一个waiter信号量确保每个时刻最少一个人没有进去就餐
while(1){
printf("Philosopher %ld is thinking\n", my_rank);
usleep(50); //think 50 ms
printf("Philosopher %ld is trying\n", my_rank);
sem_wait(&sem_fork[my_left]);
sem_wait(&sem_fork[my_right]);
printf("Philosopher %ld is eating\n", my_rank);
usleep(100); //eating 100 ms
sem_post(&sem_fork[my_left]);
sem_post(&sem_fork[my_right]);
}
sem_post(&sem_waiter);
return NULL;
}
//解锁方法2
void *Thread_method2(void *rank)
{
long my_rank = (long)rank;
long my_left, my_right; //表示每个哲学家左右的叉子
if(my_rank % 2 == 1){
my_left = (my_rank + 1) % thread_count;
my_right = my_rank;
}
else{
my_left = my_rank;
my_right = (my_rank + 1) % thread_count;
}
while(1){
printf("Philosopher %ld is thinking\n", my_rank);
usleep(50); //think 50 ms
printf("Philosopher %ld is trying\n", my_rank);
sem_wait(&sem_fork[my_left]);
sem_wait(&sem_fork[my_right]);
printf("Philosopher %ld is eating\n", my_rank);
usleep(100); //eating 100 ms
sem_post(&sem_fork[my_left]);
sem_post(&sem_fork[my_right]);
}
return NULL;
}
2.openmp版本
/*
openmp
无先生_ 2019.12.26
命令以以下形式:./deadlock -normal 5
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <semaphore.h>
#include <omp.h>
void *Thread_normal(void); //死锁状态
void *Thread_method1(void); //解锁方法1
void *Thread_method2(void); //解锁方法2
sem_t *sem_fork; //定义一组叉子的信号量
sem_t sem_waiter; //method1,用于确保至少一个人不能进餐
int main(int argc, char *argv[])
{
long thread;
long thread_count = strtol(argv[2], NULL, 10); //获取线程(哲学家)个数
sem_fork = malloc (thread_count * sizeof(sem_t));
sem_init(&sem_waiter, 0, 4); //初始化waiter信号量
long i;
for(i = 0; i < thread_count; i ++ ){
sem_init(&sem_fork[i], 0, 1); //初始化所有叉子信号量
}
if(strcmp(argv[1], "-normal") == 0 || strcmp(argv[1], "normal") == 0){ //进入相应的模式
# pragma omp parallel num_threads(thread_count)
Thread_normal();
}
else if(strcmp(argv[1], "-method1") == 0 || strcmp(argv[1], "method1") == 0){
# pragma omp parallel num_threads(thread_count)
Thread_method1();
}
else if(strcmp(argv[1], "-method2") == 0 || strcmp(argv[1], "method2") == 0){
# pragma omp parallel num_threads(thread_count)
Thread_method2();
}
for(i = 0; i < thread_count; i ++ ){ //删除所有叉子信号量
sem_destroy(&sem_fork[i]);
}
sem_destroy(&sem_waiter); //删除waiter信号量
free(sem_fork);
return 0;
}
//陷入死锁
void *Thread_normal(void)
{
long my_rank = omp_get_thread_num();
long thread_count = omp_get_num_threads();
long my_left, my_right; //表示每个哲学家左右的叉子
my_left = my_rank;
my_right = (my_rank + 1) % thread_count;
while(1){
printf("Philosopher %ld is thinking\n", my_rank);
usleep(50); //think 50 ms
printf("Philosopher %ld is trying\n", my_rank);
sem_wait(&sem_fork[my_left]);
sem_wait(&sem_fork[my_right]);
printf("Philosopher %ld is eating\n", my_rank);
usleep(100); //eating 100 ms
sem_post(&sem_fork[my_left]);
sem_post(&sem_fork[my_right]);
}
}
//解锁方法1
void *Thread_method1(void)
{
long my_rank = omp_get_thread_num();
long thread_count = omp_get_num_threads();
long my_left, my_right; //表示每个哲学家左右的叉子
my_left = my_rank;
my_right = (my_rank + 1) % thread_count;
sem_wait(&sem_waiter); //添加一个waiter信号量确保每个时刻最少一个人没有进去就餐
while(1){
printf("Philosopher %ld is thinking\n", my_rank);
usleep(50); //think 50 ms
printf("Philosopher %ld is trying\n", my_rank);
sem_wait(&sem_fork[my_left]);
sem_wait(&sem_fork[my_right]);
printf("Philosopher %ld is eating\n", my_rank);
usleep(100); //eating 100 ms
sem_post(&sem_fork[my_left]);
sem_post(&sem_fork[my_right]);
}
sem_post(&sem_waiter);
}
//解锁方法2
void *Thread_method2(void)
{
long my_rank = omp_get_thread_num();
long thread_count = omp_get_num_threads();
long my_first, my_second; //表示每个哲学家两次拿叉子的顺序
if(my_rank % 2 == 1){ //如果是奇数号哲学家,则先拿右边的叉子,再拿左边叉子
my_first = (my_rank + 1) % thread_count;
my_second = my_rank;
}
else{ //如果是偶数号哲学家,则先拿左边的叉子,再拿右边叉子
my_first = my_rank;
my_second = (my_rank + 1) % thread_count;
}
while(1){
printf("Philosopher %ld is thinking\n", my_rank);
usleep(50); //think 50 ms
printf("Philosopher %ld is trying\n", my_rank);
sem_wait(&sem_fork[my_first]);
sem_wait(&sem_fork[my_second]);
printf("Philosopher %ld is eating\n", my_rank);
usleep(100); //eating 100 ms
sem_post(&sem_fork[my_first]);
sem_post(&sem_fork[my_second]);
}
}