有五个哲学家,每个哲学家之间有一只筷子,必须同时获得两只筷子才能进餐。平时一个哲学家进行思考,饥饿时便试图取用其左、右最靠近他的筷子,只有在他拿到两支筷子时才能进餐。进餐完毕,放下筷子又继续思考。
在就餐时会引发两个问题:
1.死锁 :每个哲学家持有一只筷子,等待相邻哲学家释放另一只筷子而形成循环等待的状态。
2.饥饿:所有哲学家同时持有一只筷子,因无法得到两只而同时释放,发现条件满足又同时拿起,如此循环,所有哲学家处于饥饿状态。
解决办法一:
规定奇数号哲学家先拿左筷子再拿右筷子,而偶数号哲学家相反。
将是 2,3 号哲学家竞争 2号筷子,4,5 号哲学家竞争 5 号筷子。1 号哲学家不需要竞争。竞争过程中总有一方获得筷子,最后至少一个哲学家能获得两支筷子而进餐。
semaphore status[5] = { 1,1,1,1,1 };
//哲学家线程
void philosopher(int id)
{
while (true)
{
think();
if (id % 2)
{
wait(status[id]);//左边
wait(status[(id + 1) % 5]);//右边
eat();
signal(status[(id + 1) % 5]);
signal(status[id]);
}
else
{
wait(status[(id + 1) % 5]);//右边
wait(status[id]);//左边
eat();
signal(status[id]);
signal(status[(id + 1) % 5]);
}
think();
}
}
解决办法二:
要么同时拿起要么不拿,吧取两双筷子封装成原子操作。使用互斥量或者临界区实现。
mutex mt;
semaphore status[5] = { 1,1,1,1,1 };
//哲学家线程
void philosopher(int id)
{
while (true)
{
think();
unique_lock<mutex> lock(mt);//把同时拿两只筷子变成原子操作。
wait(status[id]);
wait(status[(id + 1) % 5]);
lock.unlock();
eat();
signal(status[(id + 1) % 5]);
signal(status[id]);
think();
}
}
解决方法三:
设置一个初值为 4 的信号量 r,只允许 4 个哲学家同时去拿左筷子,这样就能保证至少有一个哲学家可以就餐,不会出现饿死和死锁的现象。
semaphore sm(4);
semaphore status[5] = { 1,1,1,1,1 };
//哲学家线程
void philosopher(int id)
{
while (true)
{
think();
wait(sm);
wait(status[id]);
wait(status[(id + 1) % 5]);
eat();
signal(status[(id + 1) % 5]);
signal(status[id]);
signal(sm);
think();
}
}
另附c++下信号量简单实现。因为此问题中五只筷子属于五种资源而不是同一资源的5个,所以用5个信号量而不是用一个值为5的信号量。
struct semaphore
{
semaphore() : cnt(0) {}
semaphore(int i) :cnt(i) {}
condition_variable cv;
mutex mt;
int cnt;
};
void wait(semaphore& sm)
{
unique_lock<mutex> lock(sm.mt);
sm.cv.wait(lock, [&]()->bool { return sm.cnt > 0; });
sm.cnt--;
}
void signal(semaphore& sm)
{
unique_lock<mutex> lock(sm.mt);
sm.cnt++;
sm.cv.notify_one();
}
参考:
https://blog.youkuaiyun.com/Yun_Ge/article/details/89177918