问题描述
- 哲学家就餐问题可以这样表述,假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。哲学家就餐问题有时也用米饭和筷子而不是意大利面和餐叉来描述,因为很明显,吃米饭必须用两根筷子。
哲学家从来不交谈,这就很危险,可能产生死锁,每个哲学家都拿着左手的餐叉,永远都在等右边的餐叉(或者相反)。即使没有死锁,也有可能发生资源耗尽。例如,假设规定当哲学家等待另一只餐叉超过五分钟后就放下自己手里的那一只餐叉,并且再等五分钟后进行下一次尝试。这个策略消除了死锁(系统总会进入到下一个状态),但仍然有可能发生“活锁”。如果五位哲学家在完全相同的时刻进入餐厅,并同时拿起左边的餐叉,那么这些哲学家就会等待五分钟,同时放下手中的餐叉,再等五分钟,又同时拿起这些餐叉。
在实际的计算机问题中,缺乏餐叉可以类比为缺乏共享资源。一种常用的计算机技术是资源加锁,用来保证在某个时刻,资源只能被一个程序或一段代码访问。当一个程序想要使用的资源已经被另一个程序锁定,它就等待资源解锁。当多个程序涉及到加锁的资源时,在某些情况下就有可能发生死锁。例如,某个程序需要访问两个文件,当两个这样的程序各锁了一个文件,那它们都在等待对方解锁另一个文件,而这永远不会发生。
普通解决方法
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#define N 5
pthread_mutex_t mutex;
pthread_mutex_t forks[5] = {PTHREAD_MUTEX_INITIALIZER};
typedef struct Cthread{
pthread_t thinkerID;
int i;
}thread;
void thinking(int id){
printf("thinker %d is thinking!\n",id);
}
void eating(int id){
printf("thinker %d is eating!\n",id);
}
void take_chopstick(int id){
pthread_mutex_lock(&forks[(id+N-1)%5]);
pthread_mutex_lock(&forks[(id+1)%5]);
printf("the thinker %d take_chopsticks\n",id);
return;
}
void put_downcps(int id){
printf("the thinker %d put_downcps\n",id);
pthread_mutex_unlock(&forks[(id+N-1)%5]);
pthread_mutex_unlock(&forks[(id+1)%5]);
return;
}
void *thinker_work(void * arg){
thread *id = (thread*)arg;
printf("thinker init[%d]\n",id->i);
while(1){
thinking(id->i);
take_chopstick(id->i);
eating(id->i);
put_downcps(id->i);
}
}
int main(void){
thread *th = (thread *)malloc(5*sizeof(thread));
for(int i = 0;i < 5; i++){
th[i].thinkerID = 0;
th[i].i = i;
}
for(int i = 0;i < 5;i++){
if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i]) < 0)
printf("created pthread failed\n");
}
for(int i = 0;i< 5;i++){
pthread_join((th[i].thinkerID),NULL);
}
return 0;
}
方法一:单个执行
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#define N 5
pthread_mutex_t mutex;
pthread_mutex_t forks[5] = {PTHREAD_MUTEX_INITIALIZER};
typedef struct Cthread{
pthread_t thinkerID;
int i;
}thread;
void thinking(int id){
printf("thinker %d is thinking!\n",id);
}
void eating(int id){
printf("thinker %d is eating!\n",id);
}
void take_chopstick(int id){
pthread_mutex_lock(&forks[(id+N-1)%5]);
pthread_mutex_lock(&forks[(id+1)%5]);
printf("the thinker %d take_chopsticks\n",id);
return;
}
void put_downcps(int id){
printf("the thinker %d put_downcps\n",id);
pthread_mutex_unlock(&forks[(id+N-1)%5]);
pthread_mutex_unlock(&forks[(id+1)%5]);
return;
}
void *thinker_work(void * arg){
thread *id = (thread*)arg;
printf("thinker init[%d]\n",id->i);
while(1){
thinking(id->i);
pthread_mutex_lock(&mutex);
take_chopstick(id->i);
pthread_mutex_unlock(&mutex);
eating(id->i);
put_downcps(id->i);
}
}
int main(void){
thread *th = (thread *)malloc(5*sizeof(thread));
for(int i = 0;i < 5; i++){
th[i].thinkerID = 0;
th[i].i = i;
}
for(int i = 0;i < 5;i++){
if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i]) < 0)
printf("created pthread failed\n");
}
for(int i = 0;i< 5;i++){
pthread_join((th[i].thinkerID),NULL);
}
return 0;
}
方法二:利用信号量控制线程数量
#include<semaphore.h>
#include<pthread.h>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#define N 5
sem_t sem ;
pthread_mutex_t mutex;
pthread_mutex_t forks[5] = {PTHREAD_MUTEX_INITIALIZER};
typedef struct Cthread{
pthread_t thinkerID;
int i;
}thread;
void thinking(int id){
printf("thinker %d is thinking!\n",id);
}
void eating(int id){
printf("thinker %d is eating!\n",id);
}
void take_chopstick(int id){
pthread_mutex_lock(&forks[(id+N-1)%5]);
pthread_mutex_lock(&forks[(id+1)%5]);
printf("the thinker %d take_chopsticks\n",id);
return;
}
void put_downcps(int id){
printf("the thinker %d put_downcps\n",id);
pthread_mutex_unlock(&forks[(id+N-1)%5]);
sem_post(&sem);
pthread_mutex_unlock(&forks[(id+1)%5]);
return;
}
void *thinker_work(void * arg){
thread *id = (thread*)arg;
printf("thinker init[%d]\n",id->i);
while(1){
thinking(id->i);
sem_wait(&sem);
take_chopstick(id->i);
eating(id->i);
put_downcps(id->i);
}
}
int main(void){
thread *th = (thread *)malloc(5*sizeof(thread));
sem_init(&sem,0,4);
for(int i = 0;i < 5; i++){
th[i].thinkerID = 0;
th[i].i = i;
}
for(int i = 0;i < 5;i++){
if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i]) < 0)
printf("created pthread failed\n");
}
for(int i = 0;i< 5;i++){
pthread_join((th[i].thinkerID),NULL);
}
return 0;
}
方法三:利用回退机制进行操作
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<pthread.h>
#include<errno.h>
#define N 5
pthread_mutex_t mutex;
pthread_mutex_t forks[6] = {PTHREAD_MUTEX_INITIALIZER};
typedef struct Cthread{
pthread_t thinkerID;
int i;
}thread;
void thinking(int id){
printf("thinker %d is thinking!\n",id);
}
void eating(int id){
printf("thinker %d is eating!\n",id);
}
void *thinker_work(void * arg){
thread *id = (thread*)arg;
int left,right;
printf("thinker init[%d]\n",id->i);
switch(id->i)
{
case 0:
left = 5;
right = 1;
break;
case 1:
left = 1;
right = 2;
break;
case 2:
left = 2;
right = 3;
break;
case 3:
left = 3;
right = 4;
break;
case 4:
left = 4;
right = 5;
break;
default:
break;
}
while(1){
thinking(id->i);
sleep(2);
pthread_mutex_lock(&forks[right]);
printf("thinker %d carry the right chopstick\n",id->i);
if(pthread_mutex_trylock(&forks[left]) == EBUSY){
pthread_mutex_unlock(&forks[right]);
printf("unluckly ...\n");
continue;
}
printf("thinker %d carry chopstick %d\n",id->i,left);
eating(id->i);
sleep(2);
pthread_mutex_unlock(&forks[right]);
printf("thinker %d release the chopstick %d\n",id->i,right);
pthread_mutex_unlock(&forks[left]);
printf("thinker %d release the chopstick %d\n",id->i,left);
}
}
int main(void){
thread *th = (thread *)malloc(5*sizeof(thread));
for(int i = 0;i < 5; i++){
th[i].thinkerID = 0;
th[i].i = i;
}
for(int i = 0;i < 5;i++){
if(pthread_create(&(th[i].thinkerID),NULL,thinker_work,&th[i]) < 0)
printf("created pthread failed\n");
}
for(int i = 0;i< 5;i++){
pthread_join((th[i].thinkerID),NULL);
}
return 0;
}
方法四:对哲学家进行信号标记
#include <pthread.h>
#include <stdio.h>
#include<stdlib.h>
#include<unistd.h>
#define N 5
#define LEFT (signo+N-1)%N
#define RIGHT (signo+1)%N
#define THINK_TIME 2
#define EAT_TIME 1
enum {EATING,HUNNGRY,THINKING} state[N];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER,thinker[N];
void test(int signo){
printf("the thinker %d is trying to take the chopsticks\n",signo);
if(state[signo] == HUNNGRY &&
state[LEFT] != EATING &&
state[RIGHT] != EATING ){
state[signo] = EATING;
pthread_mutex_unlock(&thinker[signo]);
}
}
void take_chopsticks(int signo){
pthread_mutex_lock(&mutex);
state[signo]= HUNNGRY;
test(signo);
pthread_mutex_unlock(&mutex);
pthread_mutex_lock(&thinker[signo]);
}
void put_chopsticks(int signo){
pthread_mutex_lock(&mutex);
state[signo]= THINKING;
test(LEFT);
test(RIGHT);
pthread_mutex_unlock(&mutex);
}
void think(int signo){
printf("the think %d is thinking\n",signo);
sleep(THINK_TIME);
}
void eating(int signo){
printf("the think %d is eating\n",signo);
sleep(EAT_TIME);
}
void *work(void * argc){
int i = *(int *)argc;
while(1){
think(i);
take_chopsticks(i);
eating(i);
put_chopsticks(i);
}
}
int main(void){
pthread_t th[N] = {0};
for(int i = 0;i < 5; i++)
pthread_create(&th[i],NULL,work,(void*)(&i));
for(int i = 0;i < 5; i++)
pthread_join(th[i],NULL);
return 0;
}
方法五:规定先例来防止死锁
#include<stdio.h>
#include<stdlib.h>
#include<pthread.h>
#include<unistd.h>
#define N 5
pthread_mutex_t chopsticks[N] = {PTHREAD_MUTEX_INITIALIZER};
void eat(int i){
printf("thinker %d eat eat eat\n",i);
}
void *work(void *argc){
int i = *(int*)argc;
int left = i;
int right = (i+1)%N;
while (1) {
printf("哲学家%d正在思考问题\n", i);
printf("哲学家%d饿了\n", i);
if (i % 2 == 0) {
pthread_mutex_lock(&chopsticks[right]);
pthread_mutex_lock(&chopsticks[left]);
eat(i);
pthread_mutex_unlock(&chopsticks[left]);
pthread_mutex_unlock(&chopsticks[right]);
} else {
pthread_mutex_lock(&chopsticks[left]);
pthread_mutex_lock(&chopsticks[right]);
eat(i);
pthread_mutex_unlock(&chopsticks[right]);
pthread_mutex_unlock(&chopsticks[left]);
}
}
}
int main(){
pthread_t threadID[N];
for(int i = 0;i < 5;i++)
pthread_create(&threadID[i],NULL,work,(void *)(&i));
for(int i = 0;i<5;i++)
pthread_join(threadID[i],NULL);
}