用Linux C的互斥锁机制解决哲学家就餐问题(简单实现)

17 篇文章 0 订阅

    在1971年,著名的计算机科学家艾兹格·迪科斯彻提出了一个同步问题,即假设有五台计算机都试图访问五份共享的磁带驱动器。稍后,这个问题被托尼·霍尔重新表述为哲学家就餐问题。这个问题可以用来解释死锁和资源耗尽。

   「哲学家就餐说明」:有五个哲学家共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个筷子,他们的生活方式是交替进行思考和就餐,通常,一个哲学家饥饿时就会试图去取用其左右最靠近自己的筷子,只有拿到两个筷子才能就餐,就餐完毕,放下筷子继续思考!

   但其中存在这一中问题,当五个人同时饥饿的时候,每个人拿着自己左边或右边的一只筷子,就会产生「死锁」状态,当然我们可以给每个人加上时间,等待几分钟后就必须放下手中的筷子,这种情况貌似解决饿了死锁问题,但是没法避免所谓的「活锁」,在计算机中,五条线程同时进入同一状态,拿起五只筷子。所以,这种情况在计算机资源分配就会是不合理的。

   在实际的计算机问题中,缺乏餐叉可以类比为缺乏共享资源。计算机技术是常用的是所谓的资源加锁,用来保证在某个时刻,资源只能被一个程序或一段代码访问。当一个程序想要使用的资源已经被另一个程序锁定,它就等待资源解锁。当多个程序涉及到加锁的资源时,在某些情况下就有可能发生死锁。例如,某个程序需要访问两个文件,当两个这样的程序各锁了一个文件,那它们都在等待对方解锁另一个文件,而这永远不会发生。

  所以在这里我们用计算机中的五个条件变量来模拟五只筷子的状态,用五条线程模拟五个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想要吃东西。如果他拿起了第五只餐叉,就有可能发生死锁。相反,如果他征求服务生同意,服务生会让他等待。这样,我们就能保证下次当两把餐叉空余出来时,一定有一位哲学家可以成功的得到一对餐叉,从而避免了死锁。




  • 3
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值