分析:
为了避免死锁,做了如下规定:每个哲学家先拿自己左手边的筷子,然后再去拿右手边的筷子,如果不能同时得到两支筷子,则该哲学家放下手中已有的筷子。这种规定依然会因为振荡而产生死锁,例如:每个哲学家都同时拿上了左手边的筷子,然后都阻塞,都释放左手筷子;然后又同时去拿左手边的筷子,这样周而复始,一直进行下去,就是因为振荡而产生的死锁。解决方法:规定其中一位哲学家先拿右手边的筷子,再拿左手边的筷子即可。这样即使最坏的情况下,也至少有一位哲学家可以拿到两支筷子,而只要有一位哲学家拿到了两支筷子,就肯定不会出现死锁。
在这里筷子是临界资源,即只能被哲学家互斥访问,因此可以将筷子作为互斥量或者信号量。五支筷子应该引入五个互斥量,或者5个信号量(初始值为1)。将五个哲学家当成五个线程,其编号为i(0、1、2、3、4),互斥量或信号量编号也为0、1、2、3、4。则按照规定:i=0~3时,先拿i,再拿i+1;i=4时,先拿0,再拿i。在此基础上,对于每一个线程,如果不能同时拿上两支筷子,则放弃已获得的筷子,则:第一次用lock函数,采用阻塞加锁;第二次拿筷子用trylock函数,采用非阻塞加锁,并且释放掉已经拿掉的筷子。
注意:互斥量、信号量既可以用于进程间同步,也可以用于线程间同步(一般来说,进程间同步信号量用的多一点);条件变量需要结合互斥量使用;读写锁用于线程间同步,而文件锁用于进程间同步。
下面线程版采用互斥量,进程版采用信号量
//线程版哲学家用餐模型
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
pthread_mutex_t mutex[5];
void *tfn( void *arg )
{
int i = (int)arg;
int ret;
while(1) {
if( i<4 )
{
pthread_mutex_lock( &mutex[i] );
ret = pthread_mutex_trylock( &mutex[i+1] );
if( ret != 0)
{
//printf("The %dth philosopher missed the second chopstick!\n", i+1);
pthread_mutex_unlock( &mutex[i] );
}else
{
printf("Very good, the %dth philosopher has a pair of chopsticks to eat food!\n", i+1);
sleep(5);
pthread_mutex_unlock( &mutex[i] );
pthread_mutex_unlock( &mutex[i+1] );
//break;
}
}else
{
pthread_mutex_lock( &mutex[0] );
ret = pthread_mutex_trylock( &mutex[i] );
if( ret != 0)
{
//printf("The %dth philosopher missed the second chopstick!\n", i+1);
pthread_mutex_unlock( &mutex[0] );
}else
{
printf("Very good, the %dth philosopher has a pair of chopsticks to eat food!\n", i+1);
sleep(5);
pthread_mutex_unlock( &mutex[i] );
pthread_mutex_unlock( &mutex[0] );
//break;
}
}
}
return NULL;
}
int main( void )
{
int i, ret;
pthread_t phi[5];
pthread_attr_t attr;
ret = pthread_attr_init( &attr );
if( ret != 0 )
{
fprintf(stderr,"pthread_attr_init error: %s.\n", strerror(ret));
exit(1);
}
ret = pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );
if( ret != 0 )
{
fprintf(stderr,"pthread_attr_detach error: %s.\n", strerror(ret));
exit(1);
}
for( i=0; i<5; i++)
{
ret = pthread_mutex_init( &mutex[i], NULL );
if( ret != 0 )
{
fprintf(stderr,"pthread_mutex_init error: %s.\n", strerror(ret));
exit(1);
}
}
for( i=0; i<5; i++)
{
ret = pthread_create( &phi[i], &attr, tfn, (void *)i );
if( ret != 0 )
{
fprintf(stderr,"pthread_create error: %s.\n", strerror(ret));
exit(1);
}
}
pthread_attr_destroy( &attr );
pthread_exit( NULL );
}
[root@localhost 02_pthread_sync_test]# ./philosophy_pthrd
Very good, the 2th philosopher has a pair of chopsticks to eat food!
Very good, the 4th philosopher has a pair of chopsticks to eat food!
Very good, the 1th philosopher has a pair of chopsticks to eat food!
Very good, the 3th philosopher has a pair of chopsticks to eat food!
Very good, the 3th philosopher has a pair of chopsticks to eat food!
Very good, the 1th philosopher has a pair of chopsticks to eat food!
Very good, the 1th philosopher has a pair of chopsticks to eat food!
Very good, the 3th philosopher has a pair of chopsticks to eat food!
Very good, the 3th philosopher has a pair of chopsticks to eat food!
Very good, the 1th philosopher has a pair of chopsticks to eat food!
//进程版哲学家用餐模型
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <stdlib.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/wait.h>
int main(void)
{
int i;
pid_t pid;
sem_t *s;
s = mmap(NULL, sizeof(sem_t)*5, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANON, -1, 0);
if (s == MAP_FAILED) {
perror("fail to mmap");
exit(1);
}
for (i = 0; i < 5; i++)
sem_init(&s[i], 0, 1); //信号量初值制定为1,信号量,变成了互斥锁
for (i = 0; i < 5; i++)
if ((pid = fork()) == 0)
break;
if (i < 5) { //子进程
int l, r;
srand(time(NULL));
if (i == 4)
l = 0, r = 4;
else
l = i, r = i+1;
while (1) {
sem_wait(&s[l]);
if (sem_trywait(&s[r]) == 0) {
printf(" %c is eating\n", 'A'+i);
sem_post(&s[r]);
}
sem_post(&s[l]);
sleep(rand() % 5);
}
exit(0);
}
for (i = 0; i < 5; i++)
wait(NULL);
for (i = 0; i < 5; i++)
sem_destroy(&s[i]);
munmap(s, sizeof(sem_t)*5);
return 0;
}