https://www.bilibili.com/video/BV1YE411D7nH?spm_id_from=333.788.videopod.episodes&vd_source=6c2daed6731190bb7d70296d6b9746bb&p=36
方法1
n个哲学家,n个筷子
创建一个初值为n-1
的信号量,保证最多只有n-1
个进程并发争抢资源,必有1
个筷子资源余留,可以1个进程拿到两支筷子,不会死锁。
这个方法的原子操作是拿取一个筷子的过程
#include <iostream>
#include <semaphore.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <fcntl.h>#define N 5using namespace std;int main(){int n = N;pid_t pid;/************* 信号量 *************///创建信号量,初值n-1;char sem_name[20] = "/my_semaphore";sem_t* sem = sem_open(sem_name, O_CREAT|O_EXCL, 0666, n-1);if (sem == SEM_FAILED){if (errno == EEXIST){// 如果信号量已存在,尝试删除它sem_unlink(sem_name);// 等待sem_unlink完成,避免竞态条件while (sem_open(sem_name, O_EXCL, 0666, n-1 )!= SEM_FAILED) {usleep(1000); // 等待1毫秒}sem = sem_open(sem_name, O_CREAT|O_EXCL, 0666, n-1);}}/************ 使用信号量保证拿取的原子性 ************/sem_t* sems[N]; // 为每个筷子创建互斥量for (int i = 0; i < n; i++) {sprintf(sem_name, "/sem_%d", i);sems[i] = sem_open(sems_name[i], O_CREAT | O_EXCL, 0666, 1);if (sems[i] == SEM_FAILED){if (errno == EEXIST){// 如果信号量已存在,尝试删除它sem_unlink(sems_name[i]);// 等待sem_unlink完成,避免竞态条件while (sem_open(sems_name[i], O_EXCL, 0666, n-1 )!= SEM_FAILED) {usleep(1000); // 等待1毫秒}sems[i] = sem_open(sems_name[i], O_CREAT|O_EXCL, 0666, 1);}}}/********** 哲学家子进程 ***********/int i;//创建n个哲学家子进程for (i = 0; i < n; i++){pid = fork();if(pid == 0)break;if (pid < 0)perror("fork");}// 哲学家子进程if(pid == 0){// 使用信号量取得进餐权限sem_wait(sem);// 左右筷子编号int l = i;int r = (i+1)%n;// 等待筷子sem_wait(sems[l]);sem_wait(sems[r]);// 模拟进餐sleep(1);cout << "哲学家 " << i << " 号进餐完毕" << endl;// 释放筷子sem_post(sems[l]);sem_post(sems[r]);//释放信号量sem_post(sem);// 关闭信号量if (sem_close(sem) == -1) {perror("sem_close");exit(1);}exit(0);}// 子进程回收while (wait(NULL) > 0);cout << "哲学家已都进餐完毕" << endl;/************* 信号量回收 *************/// 完成后关闭信号量if (sem_close(sem) == -1) {perror("sem_close");exit(EXIT_FAILURE);}// 删除信号量(可选,如果不再需要)if (sem_unlink(sem_name) == -1) {perror("sem_unlink");exit(EXIT_FAILURE);}// 关闭和删除信号量for (int i = 0; i < n; i++) {sprintf(sem_name, "/sem_%d", i);if (sem_close(sems[i]) == -1) {perror("sem_close");}if (sem_unlink(sem_name) == -1) {perror("sem_unlink");}}return 0;
}
方法2
#include <iostream>
#include <semaphore.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <fcntl.h>#define N 5using namespace std;int main(){int n = N;pid_t pid;char sems_name[N][20];/************ 使用信号量保证拿取的原子性 ************/sem_t* sems[N]; // 为每个筷子创建互斥量for (int i = 0; i < n; i++) {sprintf(sems_name[i], "/sem_%d", i);sems[i] = sem_open(sems_name[i], O_CREAT | O_EXCL, 0666, 1);if (sems[i] == SEM_FAILED){if (errno == EEXIST){// 如果信号量已存在,尝试删除它sem_unlink(sems_name[i]);// 等待sem_unlink完成,避免竞态条件while (sem_open(sems_name[i], O_EXCL, 0666, n-1 )!= SEM_FAILED) {usleep(1000); // 等待1毫秒}sems[i] = sem_open(sems_name[i], O_CREAT|O_EXCL, 0666, 1);}}}/********** 哲学家子进程 ***********/int i;//创建n个哲学家子进程for (i = 0; i < n; i++){pid = fork();if(pid == 0)break;if (pid < 0)perror("fork");}// 哲学家子进程if(pid == 0){// 左右筷子编号int l = i;int r = (i+1)%n;// 等待筷子if(i%2==0){sem_wait(sems[l]);sem_wait(sems[r]);}else{sem_wait(sems[r]);sem_wait(sems[l]);}// 模拟进餐sleep(1);cout << "哲学家 " << i << " 号进餐完毕" << endl;// 释放筷子sem_post(sems[l]);sem_post(sems[r]);if (sem_close(sems[i]) == -1) {perror("sem_close");exit(1);}exit(0);}// 子进程回收while (wait(NULL) > 0);cout << "哲学家已都进餐完毕" << endl;/************* 信号量回收 *************/for (int i = 0; i < n; i++) {if (sem_close(sems[i]) == -1) {perror("sem_close");}if (sem_unlink(sems_name[i]) == -1) {perror("sem_unlink");}}return 0;
}
方法3
对拿两个筷子这一整个过程进行互斥操作,保证总有一个进程完整的拿到两个筷子。
#include <iostream>
#include <semaphore.h>
#include <unistd.h>
#include <sys/shm.h>
#include <sys/sem.h>
#include <sys/wait.h>
#include <fcntl.h>#define N 5using namespace std;int main(){int n = N;pid_t pid;const char* sem_name = "/my_semaphore";char sems_name[N][20];/************* 信号量 *************///创建 拿一双筷子这个过程 的互斥量sem_t* sem = sem_open(sem_name, O_CREAT|O_EXCL, 0666, 1);if (sem == SEM_FAILED){if (errno == EEXIST){// 如果信号量已存在,尝试删除它sem_unlink(sem_name);// 等待sem_unlink完成,避免竞态条件while (sem_open(sem_name, O_EXCL, 0666, n-1 )!= SEM_FAILED) {usleep(1000); // 等待1毫秒}sem = sem_open(sem_name, O_CREAT|O_EXCL, 0666, n-1);}}/************ 使用信号量保证拿取的原子性 ************/sem_t* sems[N]; // 为每个筷子创建互斥量for (int i = 0; i < n; i++) {sprintf(sems_name[i], "/sem_%d", i);sems[i] = sem_open(sems_name[i], O_CREAT | O_EXCL, 0666, 1);if (sems[i] == SEM_FAILED){if (errno == EEXIST){// 如果信号量已存在,尝试删除它sem_unlink(sem_name);// 等待sem_unlink完成,避免竞态条件while (sem_open(sem_name, O_EXCL, 0666, n-1 )!= SEM_FAILED) {usleep(1000); // 等待1毫秒}sems[i] = sem_open(sem_name, O_CREAT|O_EXCL, 0666, 1);}}}/********** 哲学家子进程 ***********/int i;//创建n个哲学家子进程for (i = 0; i < n; i++){pid = fork();if(pid == 0)break;if (pid < 0)perror("fork");}// 哲学家子进程if(pid == 0){// 使用信号量取得拿一双筷子权限sem_wait(sem);// 左右筷子编号int l = i;int r = (i+1)%n;// 等待筷子sem_wait(sems[l]);sem_wait(sems[r]);// 模拟进餐sleep(1);cout << "哲学家 " << i << " 号进餐完毕" << endl;// 释放筷子sem_post(sems[l]);sem_post(sems[r]);//释放拿一双筷子这个过程 的信号量sem_post(sem);// 子进程关闭信号量if (sem_close(sem) == -1) {perror("sem_close");exit(1);}if (sem_close(sems[i]) == -1) {perror("sem_close");exit(1);}exit(0);}// 子进程回收while (wait(NULL) > 0);cout << "哲学家已都进餐完毕" << endl;/************* 信号量回收 *************/// 完成后关闭信号量if (sem_close(sem) == -1) {perror("sem_close");exit(EXIT_FAILURE);}if (sem_unlink(sem_name) == -1) {perror("sem_unlink");exit(EXIT_FAILURE);}for (int i = 0; i < n; i++) {if (sem_close(sems[i]) == -1) {perror("sem_close");}if (sem_unlink(sems_name[i]) == -1) {perror("sem_unlink");}}return 0;
}