用 C 语言实现哲学家就餐问题:互斥锁和信号量解决方案
哲学家就餐问题:互斥锁和信号量解决方案
哲学家就餐问题是一个经典的并发编程问题,用来演示资源竞争和死锁的可能性。问题描述如下:
5 个哲学家围着圆桌坐,全体到达后开始讨论。在讨论的间隙哲学家进餐,每人进餐时都需使用一双筷子,所有哲学家左右手筷子都拿到后才能进餐。哲学家的人数、餐桌上的布置自行设定,实现筷子的互斥使用算法的程序实现。
本文将使用 C 语言实现哲学家就餐问题,并提供两种解决方案:互斥锁和信号量。
使用互斥锁解决哲学家就餐问题
算法描述:
- 创建一个互斥锁数组,用于表示每个筷子的状态。
- 每个哲学家进程需要获取左右两支筷子的互斥锁才能进餐。
- 当哲学家吃完饭后,释放两支筷子的互斥锁。
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define N 5 // 哲学家的数量
pthread_mutex_t forks[N]; // 筷子的互斥锁
void *philosopher(void *arg) {
int num = *(int*)arg; // 哲学家的编号
int left = num; // 左手的筷子编号
int right = (num + 1) % N; // 右手的筷子编号
while (1) {
// 哲学家思考
printf('哲学家 %d 开始思考\n', num);
sleep(rand() % 3);
// 哲学家饿了,准备就餐
printf('哲学家 %d 饿了,准备就餐\n', num);
// 先尝试获取左手的筷子
pthread_mutex_lock(&forks[left]);
printf('哲学家 %d 拿起了左手的筷子 %d\n', num, left);
// 再尝试获取右手的筷子
if (pthread_mutex_trylock(&forks[right]) == 0) {
printf('哲学家 %d 拿起了右手的筷子 %d\n', num, right);
printf('哲学家 %d 开始进餐\n', num);
// 哲学家进餐
sleep(rand() % 3);
// 哲学家就餐完毕,释放筷子
printf('哲学家 %d 进餐完毕,放下筷子 %d 和 %d\n', num, left, right);
pthread_mutex_unlock(&forks[left]);
pthread_mutex_unlock(&forks[right]);
} else {
// 右手的筷子被其他哲学家占用,放下左手的筷子
printf('哲学家 %d 拿起右手的筷子 %d 失败\n', num, right);
printf('哲学家 %d 放下左手的筷子 %d\n', num, left);
pthread_mutex_unlock(&forks[left]);
}
}
return NULL;
}
int main() {
pthread_t philosophers[N];
int nums[N] = {0, 1, 2, 3, 4}; // 哲学家的编号
// 初始化互斥锁
for (int i = 0; i < N; i++) {
pthread_mutex_init(&forks[i], NULL);
}
// 创建哲学家进程
for (int i = 0; i < N; i++) {
pthread_create(&philosophers[i], NULL, philosopher, (void*)&nums[i]);
}
// 等待哲学家进程结束
for (int i = 0; i < N; i++) {
pthread_join(philosophers[i], NULL);
}
// 销毁互斥锁
for (int i = 0; i < N; i++) {
pthread_mutex_destroy(&forks[i]);
}
return 0;
}
使用信号量解决哲学家就餐问题
算法描述:
- 创建一个信号量数组,用于表示每个筷子的状态。初始值为 1,表示每个筷子可用。
- 每个哲学家进程需要获取左右两支筷子的信号量才能进餐。
- 当哲学家吃完饭后,释放两支筷子的信号量。
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#define N 5 // 哲学家的数量
sem_t forks[N]; // 筷子的信号量
void *philosopher(void *arg) {
int num = *(int*)arg; // 哲学家的编号
int left = num; // 左手的筷子编号
int right = (num + 1) % N; // 右手的筷子编号
while (1) {
// 哲学家思考
printf('哲学家 %d 开始思考\n', num);
sleep(rand() % 3);
// 哲学家饿了,准备就餐
printf('哲学家 %d 饿了,准备就餐\n', num);
// 先尝试获取左手的筷子
sem_wait(&forks[left]);
printf('哲学家 %d 拿起了左手的筷子 %d\n', num, left);
// 再尝试获取右手的筷子
if (sem_trywait(&forks[right]) == 0) {
printf('哲学家 %d 拿起了右手的筷子 %d\n', num, right);
printf('哲学家 %d 开始进餐\n', num);
// 哲学家进餐
sleep(rand() % 3);
// 哲学家就餐完毕,释放筷子
printf('哲学家 %d 进餐完毕,放下筷子 %d 和 %d\n', num, left, right);
sem_post(&forks[left]);
sem_post(&forks[right]);
} else {
// 右手的筷子被其他哲学家占用,放下左手的筷子
printf('哲学家 %d 拿起右手的筷子 %d 失败\n', num, right);
printf('哲学家 %d 放下左手的筷子 %d\n', num, left);
sem_post(&forks[left]);
}
}
return NULL;
}
int main() {
pthread_t philosophers[N];
int nums[N] = {0, 1, 2, 3, 4}; // 哲学家的编号
// 初始化信号量
for (int i = 0; i < N; i++) {
sem_init(&forks[i], 0, 1);
}
// 创建哲学家进程
for (int i = 0; i < N; i++) {
pthread_create(&philosophers[i], NULL, philosopher, (void*)&nums[i]);
}
// 等待哲学家进程结束
for (int i = 0; i < N; i++) {
pthread_join(philosophers[i], NULL);
}
// 销毁信号量
for (int i = 0; i < N; i++) {
sem_destroy(&forks[i]);
}
return 0;
}
总结
本文介绍了使用 C 语言实现哲学家就餐问题,并提供两种解决方案:互斥锁和信号量。两种方法都能够有效地避免死锁问题,但信号量方法更加灵活,可以用于更加复杂的同步问题。
注意:
- 以上代码仅供参考,实际应用中需要根据具体情况进行修改。
- 哲学家就餐问题是一个经典的并发编程问题,需要深入理解才能更好地应用于实际项目。
原文地址: https://www.cveoy.top/t/topic/o4Go 著作权归作者所有。请勿转载和采集!