通过经典的哲学家就餐实验,了解如何利用信号量来对共享资源进行互斥访问
时间: 2023-06-17 22:08:37 浏览: 46
经典的哲学家就餐问题是一个经典的并发编程问题,它描述了五个哲学家围坐在一张圆桌前,每个哲学家面前有一碗饭和一只筷子,五只筷子放在圆桌上。哲学家只有在拿到两只筷子时才能吃饭,每个哲学家都会先拿起自己左侧的筷子,再去尝试拿右侧的筷子,如果右侧的筷子被其他哲学家使用了,那么这个哲学家就需要等待,直到右侧的筷子空闲为止。
这个问题中的哲学家可以看做是线程,筷子可以看做是共享资源。由于每个哲学家都需要同时拿到两只筷子才能吃饭,因此需要使用信号量来协调线程之间的互斥访问。
具体实现可以使用五个信号量来表示五只筷子,当一个哲学家想要使用筷子时,需要先获取其左右两只筷子的信号量,如果都获取成功了,那么就可以开始进餐,吃完饭之后需要释放两只筷子的信号量,让其他哲学家可以使用。
这样就能够保证每个哲学家只有在拿到两只筷子时才能进餐,避免了死锁和饥饿等问题。
相关问题
(2)什么是哲学家就餐问题?利用信号量写出解决哲学家就餐问题的同步程序。
哲学家就餐问题是一个经典的并发问题,描述了五个哲学家围坐在桌子前,每人面前有一碗饭和一只筷子,五只筷子放在桌子中央。他们只能拿起自己左右两边的筷子来进餐,而每人吃饭时间较长,需要两只筷子。
这个问题的关键在于如何避免死锁,即每个哲学家都拿起自己左边的筷子,导致无法再拿到右边的筷子。
以下是一个使用信号量解决哲学家就餐问题的同步程序:
```c
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <unistd.h>
#define N 5 // 哲学家数量
#define LEFT (i+N-1)%N // 左邻居
#define RIGHT (i+1)%N // 右邻居
#define THINKING 0 // 思考
#define HUNGRY 1 // 饥饿
#define EATING 2 // 就餐
int state[N]; // 哲学家状态
pthread_mutex_t mutex; // 互斥锁
sem_t sem[N]; // 信号量
void *philosopher(void *arg) {
int i = *(int *)arg;
while (1) {
printf("Philosopher %d is thinking...\n", i);
sleep(rand() % 5);
printf("Philosopher %d is hungry...\n", i);
state[i] = HUNGRY;
test(i); // 尝试取得两只筷子
sem_wait(&sem[i]); // 等待信号量
printf("Philosopher %d is eating...\n", i);
sleep(rand() % 5);
printf("Philosopher %d has finished eating...\n", i);
sem_post(&sem[LEFT]); // 放回左侧筷子
sem_post(&sem[RIGHT]); // 放回右侧筷子
}
}
void test(int i) {
if (state[i] == HUNGRY && state[LEFT] != EATING && state[RIGHT] != EATING) {
state[i] = EATING;
sem_post(&sem[i]); // 释放信号量
}
}
int main() {
pthread_t tid[N];
int i, id[N];
for (i = 0; i < N; i++) {
state[i] = THINKING;
sem_init(&sem[i], 0, 0);
id[i] = i;
}
pthread_mutex_init(&mutex, NULL);
for (i = 0; i < N; i++) {
pthread_create(&tid[i], NULL, philosopher, &id[i]);
}
for (i = 0; i < N; i++) {
pthread_join(tid[i], NULL);
}
return 0;
}
```
在这个程序中,每个哲学家(线程)都有一个状态,包括 THINKING(思考)、HUNGRY(饥饿)和 EATING(就餐)。每个筷子(信号量)初始值为 0,表示不可用。当一个哲学家饥饿时,它会试图取得左右两只筷子,如果两只筷子都可用,那么它就开始就餐;否则,它会等待信号量,直到两只筷子都可用。就餐结束后,哲学家会放回两只筷子,这样其他哲学家就可以使用它们了。
信号量解决哲学家问题代码C语言
哲学家问题可以使用信号量来解决。下面是一个使用 C 语言编写的哲学家问题的解决方案:
```c
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
#define N 5 // 哲学家的数量
#define LEFT (i + N - 1) % N // 左边的哲学家编号
#define RIGHT (i + 1) % N // 右边的哲学家编号
sem_t chopsticks[N]; // 信号量数组,表示筷子的状态
sem_t mutex; // 互斥信号量,保证哲学家就餐的互斥
void *philosopher(void *arg) {
int i = *(int *)arg;
int left = LEFT;
int right = RIGHT;
while (1) {
// 哲学家思考
printf("Philosopher %d is thinking.\n", i);
// 等待左边的筷子
sem_wait(&chopsticks[left]);
// 等待右边的筷子
sem_wait(&chopsticks[right]);
// 获取互斥锁
sem_wait(&mutex);
// 哲学家就餐
printf("Philosopher %d is eating.\n", i);
// 释放互斥锁
sem_post(&mutex);
// 放下左边的筷子
sem_post(&chopsticks[left]);
// 放下右边的筷子
sem_post(&chopsticks[right]);
}
}
int main() {
pthread_t tid[N];
int index[N];
// 初始化互斥信号量
sem_init(&mutex, 0, 1);
// 初始化筷子信号量
for (int i = 0; i < N; i++) {
sem_init(&chopsticks[i], 0, 1);
}
// 创建哲学家线程
for (int i = 0; i < N; i++) {
index[i] = i;
pthread_create(&tid[i], NULL, philosopher, &index[i]);
}
// 等待哲学家线程结束
for (int i = 0; i < N; i++) {
pthread_join(tid[i], NULL);
}
// 销毁信号量
sem_destroy(&mutex);
for (int i = 0; i < N; i++) {
sem_destroy(&chopsticks[i]);
}
return 0;
}
```
在这个代码中,我们使用了一个互斥信号量 `mutex` 来保证哲学家之间的就餐互斥。每个哲学家都需要先获取左边的筷子,再获取右边的筷子,然后才能进餐。如果一个筷子被其他哲学家占用,则当前哲学家会等待,直到可以获取到筷子。就餐完成后,哲学家会先放下左边的筷子,再放下右边的筷子,然后继续思考。