哲学家就餐问题是经典的死锁问题
什么是死锁?
多个进程或线程相互等待对方的资源,在等到对方资源之前,也不会释放自己的资源,这样造成循环等待的现象,若无外力作用,他们都无法推进下去,这样就产生了死锁。
死锁产生的必要条件:
- 互斥性
- 请求并保持
- 不可剥夺
- 循环等待
防止死锁的办法:
- 一次性分够(破坏请求并保持的条件)
- 资源可剥夺(破坏不可剥夺条件)
- 资源有序分配(破坏循环等待条件)
这几种方法只是理论上的,基本不可能实现
我们只能尽量的避免死锁
经典的死锁避免算法:银行家算法
银行家算法了解一下:(四句话)
- 当顾客的资金需求少于银行家现有的资金总量时,接纳该顾客
- 顾客可以分期贷款,但贷款总量不能超过最大需求量
- 当银行家现有资金不能满足顾客需求时,可延迟交付,但总能在有限时间内让顾客得到贷款
- 当顾客使用完全部资金后,一定要在有限时间内gui归还全部资金。
下来我们具体看一下哲学家就餐问题如何解决:
为了不产生死锁,我们必须保证拿左筷子和拿右筷子这两步是一个原子操作,此时我们就可以借助信号量来实现,因为semop()这个函数具有原子性。
具体步骤如下:
1.信号量集中设置5个信号量
2.都设置初值为1(表示5个筷子的资源可用数)
3.除父进程外再创建4个子进程,每个进程代表一个哲学家
4.每个进程都先申请自己需要的两个筷子的资源(P操作)
5.使用结束后释放(V操作)
代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <unistd.h>
union semun
{
int val;
};
int semid;
//P操作
void get2kz(int no)
{
struct sembuf sb[2]={
{no,-1,0},
{(no+1)%5,-1,0}
};
semop(semid,sb,2);
}
//V操作
void put2kz(int no)
{
struct sembuf sb[2]={
{no,1,0},
{(no+1)%5,1,0}
};
semop(semid,sb,2);
}
void fun(int no)
{
while(1)
{
printf("%d 号哲学家正在思考...\n",no);
sleep(rand()%3);
get2kz(no);//请求两个筷子资源
printf("%d 号哲学家开始就餐\n",no);
sleep(rand()%3);
printf("%d 号哲学家就餐完毕\n",no);
put2kz(no);//释放两个筷子的资源
}
}
int main()
{
srand(getpid());
//创建一个信号量集,包含五个信号量,代表5个筷子的资源
semid=semget(ftok(".",1),5,IPC_CREAT|0644);
if(semid<0)
{
perror("semget");
exit(1);
}
int i=0;
//为信号量集中的每个信号量赋初值1
union semun su={1};
for(i=0;i<5;i++)
{
semctl(semid,i,SETVAL,su);
}
//创建四个子进程,代表每个哲学家
int no=0;
for(i=1;i<5;i++)
{
if(fork()==0)//子进程
{
no=i;
break;
}
}
fun(no);
return 0;
}