【线程同步三部曲】之哲学家进餐问题,c语言实现

文章目录

    • 问题描述
    • 解决方案
      • 一、一个最简单但不安全的方案
        • 方案描述
        • 方案代码
      • 二、对第一个方案的改正
        • 方案描述
        • 方案代码

问题描述

背景描述
有五个哲学家,他们的生活方式是交替地进行思考和进餐,哲学家们共用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有五个碗和五支筷子,平时哲学家进行思考,饥饿时便试图取其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐,该哲学家进餐完毕后,放下左右两只筷子又继续思考。
【线程同步三部曲】之哲学家进餐问题,c语言实现_第1张图片

约束条件:
(1)只有拿到两只筷子时,哲学家才能吃饭。
(2)如果筷子已被别人拿走,则必须等别人吃完之后才能拿到筷子。
(3)任一哲学家在自己未拿到两只筷子吃完饭前,不会放下手中已经拿到的筷子。

解决方案

我们要实现的是每个哲学家都有机会拿到左右两把叉子进行用餐,那么,可以明确的是叉子成为相邻哲学家进餐的竞争条件。接下来,我们通过一些操作实现哲学家间取叉子的同步而不产生死锁。

一、一个最简单但不安全的方案

方案描述

【线程同步三部曲】之哲学家进餐问题,c语言实现_第2张图片

如上图,对所有哲学家和叉子进行编号,每个哲学家右手的叉子的编号与哲学家编号相同。进餐时,每个哲学家先取得右手的叉子,再取得左手的叉子,方可开始进餐。

下图是对上述方案的流程描述
【线程同步三部曲】之哲学家进餐问题,c语言实现_第3张图片
到这,不妨思考几秒钟,这个方案会产生什么问题呢?
【线程同步三部曲】之哲学家进餐问题,c语言实现_第4张图片
好了,揭晓漏洞:
这种实现是有可能产生死锁的,即所有哲学家都拿着右手边的叉子,都在等待左手边的叉子,于是每个人都干等着,谁都吃不到饭。

方案代码

#include 
#include 
#include 
#include 
#include 
#include  


#define NUM 5

int eaters = 0;

//-------------------一些信号量----------------
sem_t sem_fork[NUM];
sem_t sem_eaters;
void sem_mutex_init()
{
	int i;
	for(i = 0;i < NUM; ++i)
		if(sem_init(&sem_fork[i],0,1) == -1)
		{
			printf("sem_init失败,退出!\n");
			exit(1);
		}
	if(sem_init(&sem_eaters,0,1) == -1)
	{
		printf("sem_init失败,退出!\n");
		exit(1);
	}
	return ;
}

void philosopher()
{
	for(;;)
	{
		sleep(3);
		
		pthread_t tid = pthread_self();
		tid %= 5;//这是给线程编号从0-4的一种比较简单的方法,使得和sem_fork[0] -sem_forks[4]对应
		//先拿右手边的叉子,再拿左手边的叉子
		printf("%d号哲学家正在思考...\n",tid);
		sem_wait(&sem_fork[tid]);
		sem_wait(&sem_fork[(tid+1)%5]);


		sem_wait(&sem_eaters);
		eaters++;

		printf("%d号哲学家正在用餐,目前有%d个用餐者\n",tid,eaters);
		//吃完了
		sem_post(&sem_eaters);
		
		

		sem_post(&sem_fork[(tid+1)%5]);
		sem_post(&sem_fork[tid]);

		sem_wait(&sem_eaters);
		eaters--;
		sem_post(&sem_eaters);

	}
}

int main(void)
{
	int i;
	pthread_t philosopher_threads[NUM];
	for(i = 0; i < NUM; ++i)
	{
		if(pthread_create(&philosopher_threads[i],NULL,philosopher,philosopher_threads) != 0)
		{
			printf("创建第%d个哲学家进程失败!\n",i+1);
			exit(1);
		}
	}

	
	sem_mutex_init();
	

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

	return 0;

}

二、对第一个方案的改正

方案描述

此方案和第一个方案大同小异。那么,改正方案通过“单双号限流”避免死锁:双号线程先拿右手边叉子,拿左手边叉子;单号线程先拿左手边叉子再拿右手边叉子。
【线程同步三部曲】之哲学家进餐问题,c语言实现_第5张图片
脑补一下,这样就不会产生死锁了,对吧

方案代码

代码和上一个方案的区别就在philosopher函数里对线程单双号进行了区别对待


#include 
#include 
#include 
#include 
#include 
#include  


#define NUM 5

int eaters = 0;

//-------------------一些信号量----------------
sem_t sem_fork[NUM];
sem_t sem_eaters;
void sem_mutex_init()
{
	int i;
	for(i = 0;i < NUM; ++i)
		if(sem_init(&sem_fork[i],0,1) == -1)
		{
			printf("sem_init失败,退出!\n");
			exit(1);
		}
	if(sem_init(&sem_eaters,0,1) == -1)
	{
		printf("sem_init失败,退出!\n");
		exit(1);
	}
	return ;
}

void philosopher()
{
	for(;;)
	{
		sleep(3);
		
		pthread_t tid = pthread_self();
		tid %= 5;//这是给线程编号从0-4的一种比较简单的方法,使得和sem_fork[0] -sem_forks[4]对应
		//先拿右手边的叉子,再拿左手边的叉子
		if(tid % 2 == 1)
		{
			printf("%d号哲学家正在思考...\n",tid);
			sem_wait(&sem_fork[tid]);
			sem_wait(&sem_fork[(tid+1)%5]);


			sem_wait(&sem_eaters);
			eaters++;

			printf("%d号哲学家正在用餐,目前有%d个用餐者\n",tid,eaters);
			//吃完了
			sem_post(&sem_eaters);
			
			sem_post(&sem_fork[(tid+1)%5]);
			sem_post(&sem_fork[tid]);

			sem_wait(&sem_eaters);
			eaters--;
			sem_post(&sem_eaters);

		}
		else
		{
			printf("%d号哲学家正在思考...\n",tid);
			sem_wait(&sem_fork[(tid+1)%5]);
			sem_wait(&sem_fork[tid]);
			


			sem_wait(&sem_eaters);
			eaters++;

			printf("%d号哲学家正在用餐,目前有%d个用餐者\n",tid,eaters);
			//吃完了
			sem_post(&sem_eaters);
			
					
			sem_post(&sem_fork[tid]);
			sem_post(&sem_fork[(tid+1)%5]);
			sem_wait(&sem_eaters);
			eaters--;
			sem_post(&sem_eaters);

		}
		
		
	}
}

int main(void)
{
	int i;
	pthread_t philosopher_threads[NUM];
	for(i = 0; i < NUM; ++i)
	{
		if(pthread_create(&philosopher_threads[i],NULL,philosopher,philosopher_threads) != 0)
		{
			printf("创建第%d个哲学家进程失败!\n",i+1);
			exit(1);
		}
	}

	
	sem_mutex_init();
	

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

	return 0;

}

笔者才疏学浅,若同学们有更好的想法请不吝赐教,一起探讨哈!

首次编辑 2020-4-26 23:10:58

你可能感兴趣的:(操作系统)