分析:
为了避免死锁,做了如下规定:每个哲学家先拿自己左手边的筷子,然后再去拿右手边的筷子,如果不能同时得到两支筷子,则该哲学家放下手中已有的筷子。这种规定依然会因为振荡而产生死锁,例如:每个哲学家都同时拿上了左手边的筷子,然后都阻塞,都释放左手筷子;然后又同时去拿左手边的筷子,这样周而复始,一直进行下去,就是因为振荡而产生的死锁。解决方法:规定其中一位哲学家先拿右手边的筷子,再拿左手边的筷子即可。这样即使最坏的情况下,也至少有一位哲学家可以拿到两支筷子,而只要有一位哲学家拿到了两支筷子,就肯定不会出现死锁。
在这里筷子是临界资源,即只能被哲学家互斥访问,因此可以将筷子作为互斥量或者信号量。五支筷子应该引入五个互斥量,或者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;elsel = 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;
}