我应该如何使用条件变量解决哲学家就餐问题?

Posted

技术标签:

【中文标题】我应该如何使用条件变量解决哲学家就餐问题?【英文标题】:How should I solve starving in Dinning Philosophers problem using conditional variables? 【发布时间】:2019-09-29 13:46:04 【问题描述】:

我用condition_variables写了一个模拟哲学家就餐的简单程序,问题是有些哲学家从来没有机会吃饭。

我尝试在 _condVar.Wait lambda 函数中放置断点,以确保 putdownForks 方法通知其他等待线程,我确实这样做了,但我仍然不明白为什么其他线程永远不会吃东西。

#include <condition_variable>
#include <iostream>
#include <thread>
#include <unistd.h>

#define thread_num 5
#define HUNGRY 0
#define EATING 1
#define THINKING 2

using namespace std;

thread _threads[thread_num];
mutex _mutex;
condition_variable _condVar;
int _states[thread_num];
bool _isCancelRequested;

void think(int id)
    usleep(rand() % 1000000 + 2000000);

void eat(int id)
    usleep(rand() % 1000000 + 2000000);


void putdownForks(int id)   
    
        unique_lock<mutex> lck(_mutex);          
        _states[id] = THINKING;
    
    _condVar.notify_all();    


void pickupForks(int id)
    int leftNeighbourId = (id -1 )%thread_num;
    if(leftNeighbourId<0)leftNeighbourId+=thread_num;
    int rightNeighbourId= (id+1)%thread_num;
    
        unique_lock<mutex> lck(_mutex);
        _states[id] = HUNGRY;
        bool isLeftNeighbourEating = _states[leftNeighbourId] == EATING;
        bool isRightNeighbourEating = _states[rightNeighbourId] == EATING;
        _condVar.wait(lck, 
            [isLeftNeighbourEating, isRightNeighbourEating]
                
                    return !(isLeftNeighbourEating || isRightNeighbourEating);
                );    
        _states[id] = EATING;
      


void philosopher(int id)
    while(!_isCancelRequested)
        think(id);
        pickupForks(id);
        eat(id);
        putdownForks(id);
    


void print_info()

    
        unique_lock<mutex> lck(_mutex);
        for(int i=0;i<thread_num;i++)
        
            cout << "Id: "<< i<< " State: "<< _states[i]<<endl;
        
        cout << endl;
    


void info()
    while (!_isCancelRequested)
    
        usleep(1000000);
        print_info();
        


int main()
    for(int i=0; i<thread_num;i++)
        _threads[i]= thread(philosopher,i);
    
    thread info_thread(info);

    for(int i=0; i<thread_num;i++)
        _threads[i].join();
      
    info_thread.join();

这是状态随时间变化的输出

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 1
Id: 4 State: 0

Id: 0 State: 1
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

Id: 0 State: 2
Id: 1 State: 0
Id: 2 State: 0
Id: 3 State: 2
Id: 4 State: 0

如您所见,id 为 1、2 和 4 的哲学家从不吃东西。

【问题讨论】:

【参考方案1】:

哇,这次 *** 效果对我来说很难。 通知后,等待条件需要重新评估,因此每次通知条件变量时我都需要“更新”邻居的状态。 PickupForks 方法应该是这样的:

void pickupForks(int id)
    int leftNeighbourId = (id -1 )%thread_num;
    if(leftNeighbourId<0)leftNeighbourId+=thread_num;
    int rightNeighbourId= (id+1)%thread_num;
    
        unique_lock<mutex> lck(_mutex);
        _states[id] = HUNGRY;
        _condVar.wait(lck, 
            [leftNeighbourId, rightNeighbourId]
                
                    bool isLeftNeighbourEating = _states[leftNeighbourId] == EATING;
                    bool isRightNeighbourEating = _states[rightNeighbourId] == EATING;
                    return !(isLeftNeighbourEating || isRightNeighbourEating);
                );    
        _states[id] = EATING;
      

【讨论】:

以上是关于我应该如何使用条件变量解决哲学家就餐问题?的主要内容,如果未能解决你的问题,请参考以下文章

哲学家就餐问题与死锁总结

这个解决哲学家就餐问题 (dpp) 的解决方案是如何工作的?互斥量和信号量

使用信号量 (BACI) 就餐哲学家

线程学习五:哲学家就餐问题

JUC并发编程 -- ReentrantLock可重入锁(可重入 & 可打断 & 锁超时 & 锁超时-解决哲学家就餐)

从哲学家就餐问题彻底认识死锁