一张圆桌上坐着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
运行结果