您的位置:首页 > 其它

哲学家就餐问题--信号量和互斥量预防死锁

2020-01-31 21:26 120 查看

哲学家就餐问题可以采取预防死锁的方案,就是使用互斥量和信号量锁定资源。

互斥量:

对资源进行锁定的意思就是说,当一个哲学家使用叉子的时候,他首先要先把叉子锁定,然后,拿起来。这个时候如果别的哲学家也来拿相同的叉子,发现,该叉子是被互斥量锁定了,不能够拿,那么,他就不会来抢占这个叉子,而是在排等待队列中。这样就可以避免死锁的发生。

信号量:

同样,没有资源的时候,信号量的值是0,有的时候就不断地加1。“0,1”信号和互斥量是相似的。

使用互斥量预防死锁代码:

/**
*哲学家吃饭问题:几个哲学家吃饭,假定哲学家有五个人,这五个哲学家坐在一张圆桌上面,
*每个哲学家的左手旁边都放有一个叉子(fork),那么,这围城一圈的五个哲学家有五个叉子
*。每个哲学家有三种状态,thinking(思考),trying(尝试去拿叉子吃饭),eating(已
*经拿起叉子,正在吃饭)。每次吃饭需要两个叉子,也就是哲学家左右手边的叉子。
* */

/**
* 本例使用pthread库实现哲学家进餐的问题。
* */

/**
*在这个例子中使用互斥量解决死锁问题。
* */

#include <stdio.h>
#include <pthread.h>
#include <stdlib.h>
#include <sys/wait.h>//为了使用sleep()函数

#include <unistd.h>//参数的识别
#include <getopt.h> //长命令的解析

//哲学家的数目
int  Number;

//声明共享变量fork,其中fork的数目和哲学家数目是相同的
pthread_mutex_t *pfork;

//定义一个philosopher的三个状态
#define Thinking 1
#define Trying 2
#define Eating 3

int *state;

void *EatMeal();

//得到参数
void GetArg(
char* argv[] /*in*/,
int* number /*out*/
);
void main(int argc,  char* argv[])
{

int k =0;

while(k<1)
{
int option_index = 0;
int rvalue = 0;
struct option long_option[] = {
{"normal",0,0,0 },
{"method1",0,0,0},
{"method2",0,0,0}
};

rvalue = getopt_long_only(argc,argv, "a:bc::", long_option,&option_index);
switch(option_index)
{
case 0 : printf("%s\n",long_option[option_index].name);
break;
case 1 : printf("%s\n",long_option[option_index].name);
break;
case 2 : printf("%s\n",long_option[option_index].name);
break;
}
k++;
}

GetArg(argv, &Number);
pfork = malloc(Number*sizeof(pthread_mutex_t));
state = malloc(Number*sizeof(int));

//声明进程数组,每一个进程代表一个哲学家
pthread_t philosopher[Number];
int i;
//初始化每一个资源实例
for( i = 0; i < Number; i++)
{
pthread_mutex_init(&pfork[i],NULL);
}

//创建和哲学家数量想对应的进程,并且每个进程开始进行吃饭的活动
for( i = 0; i < Number; i++)
{
//记录当前进程的编号,并传递到Meal()函数中
int j = i;
pthread_create(&philosopher[i], NULL, EatMeal, &j);
printf("I am philosopher %d\n", j);
}

//将所有的进程进行Join操作。
for( i=0; i < Number; i++)
{
pthread_join(philosopher[i], NULL);
}

//退出程序
pthread_exit(0);

return ;

}

void *EatMeal(int *i)
{
//记录当前的线程id号
int id = *i;

state[id] = Thinking; //线程初始化的时候为Thinking

int leftFork = (id + Number -1) % Number;
int rightFork = (id + Number +1) % Number;

int mealTime = 5;
int mymealTime = 0;
while (mymealTime < mealTime) //每个philosopher必须吃得符合规定
{
if(state[id] == Thinking)
{
printf("Philosopher %d is thinking\n", id);
sleep(1);
state[id] = Trying;
}else if(state[id] == Trying)
{
printf("Philosopher %d is Trying\n", id);
sleep(1);
pthread_mutex_lock(&pfork[leftFork]);
pthread_mutex_lock(&pfork[rightFork]);
state[id] = Eating;
}
else
{
printf("Philosopher %d is Eating\n", id);
sleep(1);
mymealTime++;

pthread_mutex_unlock(&pfork[leftFork]);
pthread_mutex_unlock(&pfork[rightFork]);
}
}

}

void GetArg(
char * argv[],
int* number
)
{
*number = strtol(argv[1], NULL, 10);
}
View Code

使用信号量预防死锁代码:

/**
*哲学家吃饭问题:几个哲学家吃饭,假定哲学家有五个人,这五个哲学家坐在一张圆桌上面,
*每个哲学家的左手旁边都放有一个叉子(fork),那么,这围城一圈的五个哲学家有五个叉子
*。每个哲学家有三种状态,thinking(思考),trying(尝试去拿叉子吃饭),eating(已
*经拿起叉子,正在吃饭)。每次吃饭需要两个叉子,也就是哲学家左右手边的叉子。
* */

/**
* 本例使用信号量解决哲学家进餐的问题。
* 哲学家的数目和叉子的数目相等。
* 叉子是几个哲学家共享的资源,将这些资源声明为信号量,其中每一个资源为一个信号量,
* 本例中的信号量为0和1两个值,当信号量为0的时候,是不能够被另外一个进程使用,当
* 信号两为1的时候,可以被使用。
* */
#include <stdio.h>
#include <pthread.h>
#include <math.h>
#include <sys/time.h>
#include <semaphore.h>
#include <stdlib.h>
//设定哲学家的数目
int Number;

//定义哲学家的三种状态
#define Thinking 1
#define Trying 2
#define Eating 3

pthread_mutex_t mutex; //声明一个互斥量,用来在使用资源的时候锁住

sem_t *semph; //为每一个叉子定义信号量

int *state;    //记录每一个哲学家的状态

//得到参数
void GetArg(
char* argv[];
int* number;
);

void main(int argc, char* argv[])
{
GetArg(argv, &Number);
semph = malloc(Number* sizeof(sem_t));
state = malloc(Number* sizeof(int));

pthread_t philosopher[Number];     //为每一个哲学家声明一个进程

pthread_mutex_init(&mutex, NULL); //初始化互斥量

void * EatMeal(); //哲学家操作的主要函数

int i =0;
for(i = 0; i < Number; i++)
{
int j = i;
sem_init(&semph[i], 0, 1);// 初始化信号量,信号量的值为0或者1

//创建进程,每个进程执行函数EatMeal()中的操作
int res = pthread_create(&philosopher[i], NULL, EatMeal, &j);
printf("This is thread %d \n",i);

/*
//检查是否创建进程成功
if(res !=0)
{
printf("%d sucess!\n", i);
}*/
}

for(i = 0; i < Number; i++)
{
pthread_join(philosopher[i], NULL);
}

pthread_exit(0);
//sleep(100);//主要进程执行等待的任务(应该修改,直到每一个线程都被完成主进程才结束)
}

//定义哲学家吃饭的这个问题
void *EatMeal(int *j)
{
int phiD = *j; //记录哲学家的id号
int leftPhi; //记录哲学家左边的叉子的id号
int rightPhi; //记录哲学家右边的叉子的id号

leftPhi = (phiD + Number - 1) % Number;
rightPhi = (phiD + Number + 1) % Number;

state[phiD] = Thinking; //哲学家的初始状态设为 Thinking

int mymealCount = 0;

int mealCount = 1;

double time;

while(mymealCount < mealCount) //规定每个人吃饭不能超过最大吃饭次数
{

time = (rand()%9 + 1)/100.0; //得到的时间在0.01 - 0.1之间

//如果当前的状态是Thinking,那么则转化为Trying的状态
int i = state[phiD];//记录当前的状态
if(state[phiD] == Thinking)
{
printf("%d philosopher is thinking!\n", phiD);
sleep(1);//在这个状态停留一段时间
state[phiD] = Trying;

}
//如果当前的状态是Trying,则检查是否能够达到Eating的状态
else if(state[phiD] == Trying)
{
printf("%d philosopher is trying!\n",phiD);
sleep(1);//在这个状态停留一段时间
if(!sem_wait(&semph[leftPhi]))//如果能够拿起哲学家左边的叉子
{
if(!sem_wait(&semph[rightPhi]))//也能够拿起右边的叉子
{
state[phiD] = Eating;
}else//不能拿起右边的叉子
{
sem_post(&semph[leftPhi]);//放下左边的叉子
}
}
}
else //状态为 Eating
{
printf("%d philosopher is eating!\n",phiD);
sleep(1);//先吃一段时间
sem_post(&semph[leftPhi]);//放下左边的叉子
sem_post(&semph[rightPhi]);//放下右边的叉子
state[phiD] = Thinking;
mymealCount++;
}

if(i != state[phiD])
{
pthread_mutex_lock(&mutex);
printf("%d state tranformed from %d to %d\n", phiD, i, state[phiD]);
pthread_mutex_unlock(&mutex);
}

}
}

void GetArg(
char* argv[],
int* number
)
{
*number = strtol(argv[1], NULL, 10);
}
View Code

 

转载于:https://www.cnblogs.com/SevenwindMa/p/3485129.html

  • 点赞
  • 收藏
  • 分享
  • 文章举报
baiqiao3970 发布了0 篇原创文章 · 获赞 0 · 访问量 202 私信 关注
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: