PV操作大题
PV操作知识结构
哲学家进餐问题
问题描述:
有n(n ≥ \ge ≥ 3)名哲学家围坐在一张圆桌边,每名哲学家交替地就餐和思考。再圆桌中心有m(m ≥ \ge ≥ 1)个碗,每两名哲学家之间有一根筷子。每名哲学家必须取到一个碗和两侧的筷子后,才能就餐,进餐完毕,将碗和筷子放回原位,并继续思考。为使尽可能多的哲学家同时就餐,且防止出现死锁现象,请使用信号量的P、V操作[wait(),signal()操作]描述上述过程中的互斥与同步,并说明所用信号量及初始值的含义。
解法1:限制最多可以访问资源的人数
semaphore capacity = n-1; //表示最多有n-1个哲学家可争抢资源
semaphore bowl = m; //碗
semaphore chopsticks[n] = {1}; //n个筷子,
for(int i = 0; i < n; i++)
chopsticks[i] = 1; //每个哲学家一侧筷子个数为1
Process Philosopher()
{
while(true)
{
思考
P(capacity); //最多有n-1个人可以对资源进行访问
P(bowl) //申请碗
P(chopsticks[i]); //左手筷子
P(chopsticks[(i+1)%n] //右手筷子
干饭
V(bowl);
V(chopsticks[i]);
V(chopsticks[(i+1)%n];
V(capacity);
}
}
解法1优化
可以使用碗的数量m来限制访问的人数,也就是bowl这个信号量既充当资源,又起到了限制访问人数的作用。需要注意的是bowl的数量一定要小于 n
semaphore bowl; //碗
semaphore chopsticks[n] = {1}; //n个筷子,
for(int i = 0; i < n; i++)
chopsticks[i] = 1; //每个哲学家一侧筷子个数为1
bowl = min(m,n-1); //限制访问资源的人数最多为n-1
Process Philosopher()
{
while(true)
{
思考
P(bowl); //取碗
P(chopsticks[i]); //左手筷子
P(chopsticks[(i+1)%n] //右手筷子
干饭
V(chopsticks[i]);
V(chopsticks[(i+1)%n];
V(bowl);
}
}
解法2:互斥访问资源(上锁)
书本描述:仅当一名哲学家左右两边的筷子都可用时,才允许他抓起筷子。
个人理解:其实这种策略是对哲学家申请资源的整个过程进行了一个“上锁”,也就是说当哲学家们只能互斥地进行申请资源的过程,这样就保证了第一个哲学家一定是可以申请到所有的资源的。
semaphore lock = 1; //用以互斥申请资源的信号量
semaphore bowl = m; //碗
semaphore chopsticks[n] = {1}; //n个筷子,
for(int i = 0; i < n; i++)
chopsticks[i] = 1; //每个哲学家一侧筷子个数为1
Process Philosopher()
{
while(true)
{
思考
P(lock); //"上锁"
P(chopsticks[i]); //左手筷子
P(chopsticks[(i+1)%n] //右手筷子
P(bowl); //取碗
V(lock);
干饭
V(chopsticks[i]);
V(chopsticks[(i+1)%n];
V(bowl);
}
}
解法二优化
在上一个解法当中,如果除了第一个哲学家以外的其他哲学家进程执行完申请左手筷子之后发生进程调度,并发执行了一圈之后,就会出现只有第一个哲学家手里拿着能够吃饭的资源这种情况(最坏),而其他哲学家都只有左手拿了一根筷子。这与题目要求的“让尽可能多的哲学家同时就餐”不符。因此考虑如下优化,上锁的同时判断哲学家是否能够申请到所有的资源,如果能就拿,否则就不申请。
int n = 10; //筷子数
int m = 10; //碗数
semaphore lck = 1; //取资源的信号量
bool chopsticks[n]; //所有的筷子状态,false为未取走
int bowls = 0; //当前已分配出去的碗数
for(int i = 0; i < n; i++)
chopsticks[i] = false; //初始化所有的筷子为未被拿走
Progress Philosopher(int i) //第i号哲学家
{
while (true)
{
//思考
while(true)
{
P(lck); //保证同一时刻只有一个哲学家在尝试取走
if (!chopsticks[i] && !chopsticks[(i + 1) % n] && bowls < m)//能够拿到所有资源才会进行申请
{
chopsticks[i] = true; //取走左手筷子
chopsticks[(i + 1) % n] = true; //取走右手筷子
bowls++; //取碗
V(lck)
break;
}
V(lck);
}
//干饭
P(lck); //互斥访问数组
chopsticks[i] = false; //放下左手筷子
chopsticks[(i + 1) % n] = false; //放下右手筷子
bowls--; //释放碗
V(lck);
}
}