哲学家进餐问题描述
假设有五位哲学家围坐在一张圆形餐桌旁,做以下两件事情之一:吃饭,或者思考。吃东西的时候,他们就停止思考,思考的时候也停止吃东西。餐桌中间有一大碗意大利面,每两个哲学家之间有一只餐叉。因为用一只餐叉很难吃到意大利面,所以假设哲学家必须用两只餐叉吃东西。他们只能使用自己左右手边的那两只餐叉。哲学家就餐问题有时也用米饭和筷子而不是意大利面和餐叉来描述,因为很明显,吃米饭必须用两根筷子。
三个求解策略
首先肯定是使用线程来模拟哲学家的,一个线程是一个哲学家,然后要求线程在进行资源竞争时不死锁。
下面给出几种求解策略
1. 利用锁机制,每次保证一个哲学家在吃,哲学家想吃的时候,先测试一下锁,如果能获得锁,则测试一下自己的筷子以及右边的筷子能不能拿,如果能拿则拿起筷子吃,否则阻塞,哲学家思考时测试一下能不能获得互斥锁,如果能则该哲学家放下自己的筷子和右边的筷子。
2. 封装信号量,定义类Semaphore代表信号量,并封装down和up方法,当信号量执行down方法时信号量减一,如果值变为0则阻塞线程,调用up方法使信号量加一,然后定义信号量metux=4来保证同时只有四个哲学家拿筷子,信号量筷子chopsticks []={1,1,1,1,1,…,1},首先metux.down(),保证最多进4个哲学家,然后down自己的筷子,down右边的筷子,如果线程未阻塞,则可以吃饭,吃完后,up右筷子,up自己的筷子,最后metux.up()。
3. 利用封装好的信号量,使用奇偶法。防止哲学家饿死的情况,制定如下规则:
规则: 奇数号的哲学家先拿起右边的筷子再拿起左边的筷子。
偶数号哲学家先拿起左边的筷子,再拿起右边的筷子。
如果哲学家抢到一只筷子,在抢占另一只筷子时失败,则要放弃已经抢占到的资源。
左右两边都抢到筷子的哲学家,吃完放后释放资源。
实现时只需在求解策略二的基础上加个奇偶判断就可以了
C++实现 (JAVA实现的话到处都有)
C++11的thread线程类: C++11引入的跨平台的线程类
C++11 mutex互斥锁:mutex:独占的互斥量
condition_variable条件变量: 条件变量是C++11提供的另外一种用于等待的同步机制,它能阻塞一个或多个线程,直到收到另外一个线程发出的通知或者超时,才会唤醒当前阻塞的线程
代码如下
#include<iostream>
#include<thread>
#include<mutex>
#include<windows.h>
#include<chrono>
#include<condition_variable>
#include<map>
#include<functional>
#include<string>
#include<sstream>
#include<vector>
#include<functional>
#include<ctime>
#include<cstdlib>
#define random(a,b) (rand()%(b-a+1)+a)
using namespace std;
unsigned int MaxN;
namespace Method {
mutex alock;
condition_variable t;
class Semaphore{//信号量的封装 down up操作
private:
unsigned int m;
public:
Semaphore(unsigned int x):m(x){}
Semaphore(){}
Semaphore(const Semaphore& t){m = t.m;}
~Semaphore(){}
void down(){
unique_lock<mutex> locker(alock);
while(m==0){
cout<<"(thread id="<<this_thread::get_id()<<" is wait)"<<endl;
t.wait(locker);
}
m--;
}
void up(){
alock.lock();
m++;
t.notify_all();
alock.unlock();
}
bool operator == (const Semaphore &t) {
return m == t.m;
}
bool operator !=(const Semaphore &t){
return m!=t.m;
}
bool operator == (const unsigned int &t) {
return m == t;
}
bool operator != (const unsigned int &t){
return m!=t;
}
void operator = (const Semaphore &t){
m = t.m;
}
void operator = (const unsigned int &t){
m = t;
}
Semaphore operator + (const Semaphore &t){
return Semaphore(m+t.m);
}
Semaphore operator + (const unsigned int &t){
return Semaphore(m+t);
}
};
int Int(thread::id id){
stringstream ss;
ss<<id;
return std::stoi(ss.str());
}
Semaphore mutexs = 4;
Semaphore chopsticks[10000];
//求解策略部分的代码是重点
//求解策略一
auto functionA = []()->void{ //lambda表达式
auto eating = []()->void{ //获取线程ID
unique_lock<mutex> locker(alock);
int current_id = Int(this_thread::get_id())-2;
while(chopsticks[current_id]==0||chopsticks[(current_id+1)%MaxN]==0){ //是否能拿自己的筷子和右边的筷子,如果不能则阻塞
cout<<"(thread id="<<this_thread::get_id()<<" is wait)"<<endl;
t.wait(locker); //阻塞线程
}
chopsticks[current_id] = 0; //拿自己的筷子
chopsticks[(current_id+1)%MaxN] = 0; //拿右边的筷子
cout<<"Philosopher"<<current_id<<" are eating (thread id="<<this_thread::get_id()<<" is run)"<<endl;
};
auto thinking = []()->void{ //思考方法
alock.lock(); //获得锁
int current_id = Int(this_thread::get_id())-2; //当前线程
chopsticks[current_id]=1; //放下自己的筷子
chopsticks[(current_id+1)%MaxN]=1; //放下右边的筷子
t.notify_all(); //唤醒所有线程
cout<<"Philosopher"<<current_id<<" are thinking (thread id="<<this_thread::get_id()<<" is run)"<<endl;
alock.unlock(); //释放锁
};
while(true){ //无限执行线程
thinking();
chrono::milliseconds s(1000); //睡眠1s
this_thread::sleep_for(s);
eating();
}
};
//求解策略二
auto functionB = []()->void{ //lambda表达式
while(true){ //无限执行线程
mutexs.down(); //down(mutex)
size_t id = Int(this_thread::get_id())-2; //线程ID
chopsticks[id].down(); //down(chopsticks[id])
chopsticks[(id+1)%MaxN].down(); //down(chopsticks[(id+1)%MaxN])
alock.lock(); //锁一下,保证同时只有一个线程往屏幕里输出
cout<<"Philosopher"<<id<<" are eating (thread id="<<this_thread::get_id()<<" is run)"<<endl;
alock.unlock(); //释放锁
chrono::milliseconds s(1000); //睡眠1s
this_thread::sleep_for(s);
chopsticks[(id+1)%MaxN].up(); //up(chopsticks[(id+1)%MaxN])
chopsticks[id].up(); //up(chopsticks[id])
alock.lock(); //锁一下,保证同时只有一个线程往屏幕里输出
cout<<"Philosopher"<<id<<" are thinking (thread id="<<this_thread::get_id()<<" is run)"<<endl;
alock.unlock(); //释放锁
this_thread::sleep_for(s);
mutexs.up();
}
};
//求解策略三
auto functionC = []()->void{ //lambda表达式
while(true){ //无限执行线程
int id = Int(this_thread::get_id())-2; //获取当前线程
if(id%2){
//奇数
chopsticks[(id+1)%MaxN].down(); //down(chopsticks[(id+1)%MaxN])
chopsticks[id].down(); //down(chopsticks[id])
alock.lock(); //锁一下,保证同时只有一个线程往屏幕里输出
cout<<"Philosopher"<<id<<" are eating (thread id="<<this_thread::get_id()<<" is run)"<<endl;
alock.unlock(); //释放锁
chrono::milliseconds s(1000); //睡眠1s
this_thread::sleep_for(s);
chopsticks[(id+1)%MaxN].up(); //up(chopsticks[(id+1)%MaxN])
chopsticks[id].up(); //up(chopsticks[id])
}else{
//偶数
chopsticks[id].down(); //down(chopsticks[id])
chopsticks[(id+1)%MaxN].down(); //down(chopsticks[(id+1)%MaxN])
alock.lock(); //锁一下,保证同时只有一个线程往屏幕里输出
cout<<"Philosopher"<<id<<" are eating (thread id="<<this_thread::get_id()<<" is run)"<<endl;
alock.unlock(); //释放锁
chrono::milliseconds s(1000); //睡眠1s
this_thread::sleep_for(s);
chopsticks[id].up(); //up(chopsticks[id])
chopsticks[(id+1)%MaxN].up(); //up(chopsticks[(id+1)%MaxN])
}
alock.lock(); //锁一下,保证同时只有一个线程往屏幕
cout<<"Philosopher"<<id<<" are thinking (thread id="<<this_thread::get_id()<<" is run)"<<endl;
alock.unlock(); //释放锁
}
};
}
using namespace Method;
//定义哲学家类
class Philosopher{
private:
vector<function<void()> > fns;
Philosopher(){}
Philosopher(const Philosopher& t){fns = t.fns;}
~Philosopher(){}
public:
static Philosopher *instance;
static Philosopher *of(){
if(instance) return instance;
return (instance = new Philosopher());
}
void add(initializer_list<function<void()> >fs){
for(auto iter = fs.begin();iter!=fs.end();iter++){
fns.push_back(*iter);
}
}
void add(function<void()> t){
fns.push_back(t);
}
function<void() > get(size_t i){
return fns[i];
}
};
Philosopher *Philosopher::instance = 0;
int main()
{
Philosopher::of()->add({functionA,functionB,functionC});
cout<<"input Philosopher number:"<<endl;
cin>>MaxN;
cout<<"select method"<<endl;
cout<<"1.lock"<<endl;
cout<<"2.semaphore use mutex = 4"<<endl;
cout<<"3.semaphore use odd even method"<<endl;
unsigned int num;
cin>>num;
vector<thread> phils;
for(size_t i=0;i<MaxN;i++){
chopsticks[i] = 1;
}
switch (num) {
case 1:
for(size_t i=0;i<MaxN;i++){
phils.push_back(thread(Philosopher::of()->get(0)));
}
break;
case 2:
for(size_t i=0;i<MaxN;i++){
phils.push_back(thread(Philosopher::of()->get(1)));
}
break;
case 3:
for(size_t i=0;i<MaxN;i++){
phils.push_back(thread(Philosopher::of()->get(2)));
}
break;
default:
break;
}
for(auto iter = phils.begin();iter!=phils.end();iter++){
iter->join();
}
return 0;
}