C语言实战——哲学家问题

C语言实战——哲学家问题

问题描述

有五个哲学家绕着圆桌坐,每个哲学家面前有一盘面,两人之间有一支筷子,这样每个哲学家左右各有一支筷子。哲学家有2个状态,思考或者拿起筷子吃饭。如果哲学家拿到一只筷子,不能吃饭,直到拿到2只才能吃饭,并且一次只能拿起身边的一支筷子。一旦拿起便不会放下筷子直到把饭吃完,此时才把这双筷子放回原处。如果,很不幸地,每个哲学家拿起他或她左边的筷子,那么就没有人可以吃到饭了。
哲学家进餐问题是一个多线程运用的经典例子,涉及到线程同步/互斥,临界区访问问题以及死锁问题。

解决方法

方法一

  • 通过互斥信号量mutex对哲学家进餐之前取左右两侧的筷子的操作进行保护,可以防止死锁的出现
  • 也就是说,要达到的目的是,某位哲学家开始拿第一支筷子时,其他哲学家全部不准拿筷子(即使他已经饿了),只可以放下,直到这位哲学家拿到一双筷子之后,允许其他筷子被拿起,以此类推。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
 
#define N 5
 
sem_t chopsticks[N];//设置5种信号量,有5种不同类型的资源,每一种有1个,这样便于理解,因为每个哲学家需要的资源不同
 
pthread_mutex_t mutex;//定义互斥锁
 
int philosophers[N] = {0, 1, 2, 3, 4};//代表5个哲学家的编号
 
void delay (int len) 
{
	int i = rand() % len;
	int x;
	while (i > 0) 
	{
		x = rand() % len;
		while (x > 0) 
		{
			x--;
		}
		i--;
	}
}
 
void *philosopher (void* arg) 
{
	int i = *(int *)arg;
	int left = i;//左筷子的编号和哲学家的编号相同
	int right = (i + 1) % N;//右筷子的编号为哲学家编号+1
	while (1) 
	{
		printf("哲学家%d正在思考问题\n", i);
		delay(60000);
		
		printf("哲学家%d饿了\n", i);
 
		pthread_mutex_lock(&mutex);//加锁
 
		sem_wait(&chopsticks[left]);//此时这个哲学家左筷子的信号量-1之后>=0时,表示能继续执行。
		printf("哲学家%d拿起了%d号筷子,现在只有一支筷子,不能进餐\n", i, left);
		sem_wait(&chopsticks[right]);
		printf("哲学家%d拿起了%d号筷子\n", i, right);
 
		pthread_mutex_unlock(&mutex);//解锁
 
		printf("哲学家%d现在有两支筷子,开始进餐\n", i);
		delay(60000);
		sem_post(&chopsticks[left]);
		printf("哲学家%d放下了%d号筷子\n", i, left);
		sem_post(&chopsticks[right]);
		printf("哲学家%d放下了%d号筷子\n", i, right);
	}
}
 
int main (int argc, char **argv) 
{
	srand(time(NULL));
	pthread_t philo[N];
	
	//信号量初始化
	for (int i=0; i<N; i++) 
	{
		sem_init(&chopsticks[i], 0, 1);
	}
 
	pthread_mutex_init(&mutex,NULL);//初始化互斥锁
	
	//创建线程
	for (int i=0; i<N; i++) 
	{
		pthread_create(&philo[i], NULL, philosopher, &philosophers[i]);
	}
	
	//挂起线程
	for (int i=0; i<N; i++) 
	{
		pthread_join(philo[i], NULL);
	}
	
	//销毁信号量
	for (int i=0; i<N; i++) 
	{
		sem_destroy(&chopsticks[i]);
	}
 
	pthread_mutex_destroy(&mutex);//销毁互斥锁
 
	return 0;
}

方法二

  • 对他们的拿筷子的顺序进行规定
  • 规定奇数号哲学家先拿左筷子再拿右筷子,而偶数号哲学家相反。所以将是 2,3 号哲学家竞争 3 号筷子,4,5 号哲学家竞争 5 号筷子。1 号哲学家不需要竞争。最后总会有一个哲学家能获得两支筷子而进餐。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
 
#define N 5
 
sem_t chopsticks[N];//设置5种信号量,有5种不同类型的资源,每一种有1个,这样便于理解,因为每个哲学家需要的资源不同
 
int philosophers[N] = {0, 1, 2, 3, 4};//代表5个哲学家的编号
 
void delay (int len) 
{
	int i = rand() % len;
	int x;
	while (i > 0) 
	{
		x = rand() % len;
		while (x > 0) 
		{
			x--;
		}
		i--;
	}
}
 
void *philosopher (void* arg) 
{
	int i = *(int *)arg;
	int left = i;//左筷子的编号和哲学家的编号相同
	int right = (i + 1) % N;//右筷子的编号为哲学家编号+1
	while (1) {
		if(i % 2 == 0){
		printf("哲学家%d正在思考问题\n", i);
		delay(60000);
		
		printf("哲学家%d饿了\n", i);
		sem_wait(&chopsticks[right]);//此时这个哲学家左筷子的信号量-1之后>=0时,表示能继续执行。
		printf("哲学家%d拿起了%d号筷子,现在只有一支筷子,不能进餐\n", i, right);
		sem_wait(&chopsticks[left]);
		printf("哲学家%d拿起了%d号筷子, 现在有两支筷子,开始进餐\n", i, left);
		delay(60000);
		sem_post(&chopsticks[left]);
		printf("哲学家%d放下了%d号筷子\n", i, left);
		sem_post(&chopsticks[right]);
		printf("哲学家%d放下了%d号筷子\n", i, right);
		}
 
		else{
			printf("哲学家%d正在思考问题\n", i);
			delay(60000);
			
			printf("哲学家%d饿了\n", i);
			sem_wait(&chopsticks[left]);//此时这个哲学家左筷子的信号量-1之后>=0时,表示能继续执行。
			printf("哲学家%d拿起了%d号筷子,现在只有一支筷子,不能进餐\n", i, left);
			sem_wait(&chopsticks[right]);
			printf("哲学家%d拿起了%d号筷子, 现在有两支筷子,开始进餐\n", i, right);
			delay(60000);
			sem_post(&chopsticks[left]);
			printf("哲学家%d放下了%d号筷子\n", i, left);
			sem_post(&chopsticks[right]);
			printf("哲学家%d放下了%d号筷子\n", i, right);
		}
	}
}
 
int main (int argc, char **argv) 
{
	srand(time(NULL));
	pthread_t philo[N];
	
	//信号量初始化
	for (int i=0; i<N; i++) 
	{
		sem_init(&chopsticks[i], 0, 1);
	}
	
	//创建线程
	for (int i=0; i<N; i++) 
	{
		pthread_create(&philo[i], NULL, philosopher, &philosophers[i]);
	}
	
	//挂起线程
	for (int i=0; i<N; i++) 
	{
		pthread_join(philo[i], NULL);
	}
	
	//销毁信号量
	for (int i=0; i<N; i++) 
	{
		sem_destroy(&chopsticks[i]);
	}
 
	return 0;
}