C語言實戰——哲學家問題
問題描述
有五個哲學家繞着圓桌坐,每個哲學家面前有一盤面,兩人之間有一支筷子,這樣每個哲學家左右各有一支筷子。哲學家有2個狀態,思考或者拿起筷子喫飯。如果哲學家拿到一隻筷子,不能喫飯,直到拿到2只才能喫飯,並且一次只能拿起身邊的一支筷子。一旦拿起便不會放下筷子直到把飯喫完,此時才把這雙筷子放回原處。如果,很不幸地,每個哲學家拿起他或她左邊的筷子,那麼就沒有人可以喫到飯了。
哲學家進餐問題是一個多線程運用的經典例子,涉及到線程同步/互斥,臨界區訪問問題以及死鎖問題。
解決方法
方法一
- 通過互斥信號量mutex對哲學家進餐之前取左右兩側的筷子的操作進行保護,可以防止死鎖的出現
- 也就是說,要達到的目的是,某位哲學家開始拿第一支筷子時,其他哲學家全部不準拿筷子(即使他已經餓了),只可以放下,直到這位哲學家拿到一雙筷子之後,允許其他筷子被拿起,以此類推。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define N 5
sem_t chopsticks[N];//設置5種信號量,有5種不同類型的資源,每一種有1個,這樣便於理解,因爲每個哲學家需要的資源不同
pthread_mutex_t mutex;//定義互斥鎖
int philosophers[N] = {0, 1, 2, 3, 4};//代表5個哲學家的編號
void delay (int len)
{
int i = rand() % len;
int x;
while (i > 0)
{
x = rand() % len;
while (x > 0)
{
x--;
}
i--;
}
}
void *philosopher (void* arg)
{
int i = *(int *)arg;
int left = i;//左筷子的編號和哲學家的編號相同
int right = (i + 1) % N;//右筷子的編號爲哲學家編號+1
while (1)
{
printf("哲學家%d正在思考問題\n", i);
delay(60000);
printf("哲學家%d餓了\n", i);
pthread_mutex_lock(&mutex);//加鎖
sem_wait(&chopsticks[left]);//此時這個哲學家左筷子的信號量-1之後>=0時,表示能繼續執行。
printf("哲學家%d拿起了%d號筷子,現在只有一支筷子,不能進餐\n", i, left);
sem_wait(&chopsticks[right]);
printf("哲學家%d拿起了%d號筷子\n", i, right);
pthread_mutex_unlock(&mutex);//解鎖
printf("哲學家%d現在有兩支筷子,開始進餐\n", i);
delay(60000);
sem_post(&chopsticks[left]);
printf("哲學家%d放下了%d號筷子\n", i, left);
sem_post(&chopsticks[right]);
printf("哲學家%d放下了%d號筷子\n", i, right);
}
}
int main (int argc, char **argv)
{
srand(time(NULL));
pthread_t philo[N];
//信號量初始化
for (int i=0; i<N; i++)
{
sem_init(&chopsticks[i], 0, 1);
}
pthread_mutex_init(&mutex,NULL);//初始化互斥鎖
//創建線程
for (int i=0; i<N; i++)
{
pthread_create(&philo[i], NULL, philosopher, &philosophers[i]);
}
//掛起線程
for (int i=0; i<N; i++)
{
pthread_join(philo[i], NULL);
}
//銷燬信號量
for (int i=0; i<N; i++)
{
sem_destroy(&chopsticks[i]);
}
pthread_mutex_destroy(&mutex);//銷燬互斥鎖
return 0;
}
方法二
- 對他們的拿筷子的順序進行規定
- 規定奇數號哲學家先拿左筷子再拿右筷子,而偶數號哲學家相反。所以將是 2,3 號哲學家競爭 3 號筷子,4,5 號哲學家競爭 5 號筷子。1 號哲學家不需要競爭。最後總會有一個哲學家能獲得兩支筷子而進餐。
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
#include <unistd.h>
#include <pthread.h>
#include <semaphore.h>
#define N 5
sem_t chopsticks[N];//設置5種信號量,有5種不同類型的資源,每一種有1個,這樣便於理解,因爲每個哲學家需要的資源不同
int philosophers[N] = {0, 1, 2, 3, 4};//代表5個哲學家的編號
void delay (int len)
{
int i = rand() % len;
int x;
while (i > 0)
{
x = rand() % len;
while (x > 0)
{
x--;
}
i--;
}
}
void *philosopher (void* arg)
{
int i = *(int *)arg;
int left = i;//左筷子的編號和哲學家的編號相同
int right = (i + 1) % N;//右筷子的編號爲哲學家編號+1
while (1) {
if(i % 2 == 0){
printf("哲學家%d正在思考問題\n", i);
delay(60000);
printf("哲學家%d餓了\n", i);
sem_wait(&chopsticks[right]);//此時這個哲學家左筷子的信號量-1之後>=0時,表示能繼續執行。
printf("哲學家%d拿起了%d號筷子,現在只有一支筷子,不能進餐\n", i, right);
sem_wait(&chopsticks[left]);
printf("哲學家%d拿起了%d號筷子, 現在有兩支筷子,開始進餐\n", i, left);
delay(60000);
sem_post(&chopsticks[left]);
printf("哲學家%d放下了%d號筷子\n", i, left);
sem_post(&chopsticks[right]);
printf("哲學家%d放下了%d號筷子\n", i, right);
}
else{
printf("哲學家%d正在思考問題\n", i);
delay(60000);
printf("哲學家%d餓了\n", i);
sem_wait(&chopsticks[left]);//此時這個哲學家左筷子的信號量-1之後>=0時,表示能繼續執行。
printf("哲學家%d拿起了%d號筷子,現在只有一支筷子,不能進餐\n", i, left);
sem_wait(&chopsticks[right]);
printf("哲學家%d拿起了%d號筷子, 現在有兩支筷子,開始進餐\n", i, right);
delay(60000);
sem_post(&chopsticks[left]);
printf("哲學家%d放下了%d號筷子\n", i, left);
sem_post(&chopsticks[right]);
printf("哲學家%d放下了%d號筷子\n", i, right);
}
}
}
int main (int argc, char **argv)
{
srand(time(NULL));
pthread_t philo[N];
//信號量初始化
for (int i=0; i<N; i++)
{
sem_init(&chopsticks[i], 0, 1);
}
//創建線程
for (int i=0; i<N; i++)
{
pthread_create(&philo[i], NULL, philosopher, &philosophers[i]);
}
//掛起線程
for (int i=0; i<N; i++)
{
pthread_join(philo[i], NULL);
}
//銷燬信號量
for (int i=0; i<N; i++)
{
sem_destroy(&chopsticks[i]);
}
return 0;
}