哲学家问题

一张圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿的时候,才试图拿起左、 右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿到了两根筷子才可以开始进餐,当进餐完毕后,放下筷子继续思考。

#include <errno.h>
#include <stdio.h> 
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <limits.h>
#include <pthread.h>
#include <semaphore.h>

#ifndef NUM_PHILOSOPHERS
#define NUM_PHILOSOPHERS 5
#endif
#define TAB_WIDTH (8 + NUM_PHILOSOPHERS)
#define left_fork(id) (id)
#define right_fork(id) ((id+1)%NUM_PHILOSOPHERS)

enum STATE { EATING, THINKING, CHANGING, HUNG};
char *state_str[] = { "Eat", "Think", "Changing" };

/* use POSIX semaphores to control individual forks between the philosophers */
sem_t  forks[NUM_PHILOSOPHERS];  

/*  need a binary semaphore around the state printing and updating to ensure
    that it always prints a consistent state. */
sem_t  print;

/*  take as an optional command-line argument an integer indicating how many times
    each philosopher should go through his or her eat-think cycle before exiting.
    Absent this argument, the value should default to 1. */
int times = 1;

void *philosopher(void *arg);
void pick_up_forks(int id);
void put_down_forks(int id);
void eat();
void think();
void record_state(int id, enum STATE state1, int left_fork1, int right_fork1);
void display_head();
void display_content();
void display_tail();
void display_change();
void dawdle();

struct 
{
	enum STATE state;
	int left_fork;
	int right_fork;
} phil[NUM_PHILOSOPHERS];

int main(int argc, char *argv[]) 
{
	srandom(time(NULL));
	
	if(argc > 1)
		times = atoi(argv[1]);

	int i;
	int id[NUM_PHILOSOPHERS]; /* individual identifiers (see below) */
	pthread_t childid[NUM_PHILOSOPHERS]; /* ctivations for each child */ 

	/* initialize an array of ID numbers for the children.
	*/
	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		id[i] = i;
		sem_init(&forks[i], 0, 1);
		sem_init(&print, 0, 1);
		phil[i].state = CHANGING;
		phil[i].left_fork = -1;
		phil[i].right_fork = -1;
	}

	display_head();

	/* Spawn all the children */
	for (i=0;i<NUM_PHILOSOPHERS;i++) 
	{
		/* pthread create() launches a new thread running the function
		* child(), passes a poitner to the argument in id[i], and 
		* places a thread identifier in childid[i].
		*/
		int res;
		res = pthread_create(
				&childid[i], /* where to put the identifier */
				NULL, /* don’t set any special properties */ 
				philosopher, /* call the function philosopher() */
				(void*) (&id[i]) /* pass the address of id[i] */
			);

		if ( -1 == res )  /* there was an error */
		{ 
			/* report the error condition */
			fprintf(stderr,"Child %i: %s\n",i,strerror(errno));
			exit(-1); /* bail out. */
		}
	} 


	/*
	* Now wait for each child thread to finish.
	*/
	for( i = 0 ; i < NUM_PHILOSOPHERS ; i++ ) 
	{
		pthread_join(childid[i], NULL);
		sem_destroy(&forks[i]);
	}
	sem_destroy(&print);

	display_tail();

	return 0; /* exit successfully */
}

void *philosopher(void *arg)
{
	int id = *(int*)arg;
	int i = 0;

	while(i < times)
	{
		pick_up_forks(id);
		eat();
		put_down_forks(id);
		think();
		i++;
	}

	pthread_exit(NULL);
}

void pick_up_forks(int id)
{
	/*   must avoid deadlock, and, of course, must ensure that neighboring philosophers
	     are never eating simultaneously. */

	if(id & 1)  /*  even philosophers try to pick up their left-hand forks first */
	{
		record_state(id, CHANGING, -1, -1);

		if(!sem_wait(&forks[left_fork(id)]))  /* pick up one fork successfully */
			record_state(id, CHANGING, left_fork(id), -1);  

		if(!sem_wait(&forks[right_fork(id)])) /* pick up another fork successfully */
			record_state(id, EATING, left_fork(id), right_fork(id));
	}
	else  /*  odd philosophers try to pick up their right-hand forks first */
	{
		record_state(id, CHANGING, -1, -1);

		if(!sem_wait(&forks[right_fork(id)])) /* pick up one fork successfully */
			record_state(id, CHANGING, -1, right_fork(id));

		if(!sem_wait(&forks[left_fork(id)]))  /* pick up another fork successfully */
			record_state(id, EATING, left_fork(id), right_fork(id));
	}
}

void put_down_forks(int id)
{
	if(id & 1)
	{
		record_state(id, EATING, left_fork(id), right_fork(id));

		if(!sem_post(&forks[right_fork(id)]))  /* put down one fork successfully */
			record_state(id, CHANGING, left_fork(id), -1);

		if(!sem_post(&forks[left_fork(id)]))   /* put down another fork successfully */
			record_state(id, THINKING, -1, -1);
	}
	else
	{
		record_state(id, EATING, left_fork(id), right_fork(id));

		if(!sem_post(&forks[left_fork(id)]))  /* put down one fork successfully */
			record_state(id, CHANGING, -1, right_fork(id));

		if(!sem_post(&forks[right_fork(id)])) /* put down another fork successfully */
			record_state(id, THINKING, -1, -1);
	}
}

/* To make the program runs more interesting, 
   have the philosophers linger over their spaghetti for a random amount of time */
void eat()
{
	int num = rand() % NUM_PHILOSOPHERS;
	
	while(num >= 0)
	{
		dawdle();
		num--;
	}
}

void think()
{
	int num = rand() % NUM_PHILOSOPHERS;

	while(num >= 0)
	{
		dawdle();
		num--;
	}
}

void record_state(int id, enum STATE state1, int left_fork1, int right_fork1)
{
	sem_wait(&print);

	phil[id].state = state1;
	phil[id].left_fork = left_fork1;
	phil[id].right_fork = right_fork1;

	display_content();  

	sem_post(&print);
}

/* Each time a philosopher changes state, print a status line */
void display_content()
{
	int i, j;

	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		printf("| ");
		for(j=0; j<NUM_PHILOSOPHERS; j++)
		{
			if(phil[i].state == EATING || phil[i].state == CHANGING)
			{
				if(j == phil[i].left_fork || j == phil[i].right_fork)
					printf("%d", j);
				else
					printf("-");
			}
			else 
			{
				printf("-");
			}
		}


		if(phil[i].state == EATING || phil[i].state == THINKING)
		{
			printf(" %-5s ", state_str[phil[i].state]);
		}
		else
		{
			printf(" %-5s ", " ");
		}
	}
	printf("|\n");
}

void display_change()
{
	int i, j;

	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		printf("| ");
		for(j=0; j<NUM_PHILOSOPHERS; j++)
		{
			printf("-");
		}
		
		printf(" %-5s ", " ");
	}
	printf("|\n");
}

void display_head()
{
	int i, j;
	int half = TAB_WIDTH & 1 ? TAB_WIDTH/2 : TAB_WIDTH/2 - 1;

	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		printf("|");
		for(j=0; j<TAB_WIDTH; j++)
			printf("=");
	}
	printf("|\n");

	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		printf("|");
		for(j=0; j<half; j++)
			printf(" ");
		printf("%c", 'A' + i);
		for(j=0; j<TAB_WIDTH/2; j++)
			printf(" ");
	}
	printf("|\n");

	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		printf("|");
		for(j=0; j<TAB_WIDTH; j++)
			printf("=");
	}
	printf("|\n");
}

void display_tail()
{
	display_change();

	int i, j;
	for(i=0; i<NUM_PHILOSOPHERS; i++)
	{
		printf("|");
		for(j=0; j<TAB_WIDTH; j++)
			printf("=");
	}
	printf("|\n");
}

void dawdle() 
{
	/*
	* sleep for a random amount of time between 0 and 999
	* milliseconds. This routine is somewhat unreliable, since it
	* doesn’t take into account the possiblity that the nanosleep
	* could be interrupted for some legitimate reason.
	*
	* nanosleep() is part of the realtime library and must be linked
	* with –lrt
	*/
	struct timespec tv;
	int msec = (int)(((double)random() / RAND_MAX) * 1000);
	tv.tv_sec = 0;
	tv.tv_nsec = 1000000 * msec;
	if ( -1 == nanosleep(&tv, NULL) )
	{
		perror("nanosleep");
	}
}

Makefile

ROOT = ..
EXTRALIBS = -pthread

cc = gcc
prom = dine
source = dine.c
 
dine: $(source)
	$(cc) $(source) -o dine $(EXTRALIBS)

dine15: $(source)
	$(cc) $(source) -DNUM_PHILOSOPHERS=15 -o dine15 $(EXTRALIBS)

dine6: $(source)
	$(cc) $(source) -DNUM_PHILOSOPHERS=6 -o dine6 $(EXTRALIBS)

clean:
	rm -f $(prom)
	rm -f dine15
	rm -f dine6
	

运行结果
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值