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

Posted

技术标签:

【中文标题】这个解决哲学家就餐问题 (dpp) 的解决方案是如何工作的?互斥量和信号量【英文标题】:How is this solution for dining philosophers problem (dpp) working? Mutex and semaphores 【发布时间】:2019-10-28 12:43:40 【问题描述】:

我正在尝试解决哲学家就餐问题问题:https://en.wikipedia.org/wiki/Dining_philosophers_problem),我通过下面的代码找到了解决方案。解决方案使用信号量和一个互斥体。我自己实现了忙等待简单信号量,因为 C++ 不提供信号量。我不明白在take_forksput_forks 函数中锁定互斥锁的目的是什么。

我试图找到我的问题的答案,但我找不到。所以我在***中问。

我的问题是:

    take_forksput_forks 函数中互斥锁的目的是什么? (什么会导致竞态条件发生?) 此解决方案的名称是什么?这是仲裁解决方案吗?

这是我的代码

#include <mutex>
#include <thread>

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

typedef int sem_t;
void sem_up(sem_t* semaphore) 
    (*semaphore)++;

void sem_down(sem_t* semaphore) 
    while (*semaphore == 0) 
    (*semaphore)--;


std::mutex mutualExclusion;
char philosopherState[N] = THINKING;
sem_t philosopherSemaphore[N] =  0 ;

void test(short i) 
    if (philosopherState[i] == HUNGRY && philosopherState[(i + 1) % N] != EATING && philosopherState[(i + N - 1) % N] != EATING) 
        philosopherState[i] = EATING;
        sem_up(&philosopherSemaphore[i]);
    


void think(short p) 
    //some code

void eat() 
        //some code


void take_forks(short i) 
    ::mutualExclusion.lock();
    philosopherState[i] = HUNGRY;
    test(i);
    ::mutualExclusion.unlock();
    sem_down(&philosopherSemaphore[i]);

void put_forks(short i) 
    ::mutualExclusion.lock();
    philosopherState[i] = THINKING;
    test((i + 1) % N);
    test((i + N - 1) % N);
    ::mutualExclusion.unlock();


void philosopher(short i) 
    while (1) 
        think();
        take_forks(i);
        eat();
        put_forks(i);
    

我希望互斥锁必须在 test 函数中,因为这是我发现的竞争条件的唯一原因。

感谢任何答案和建议!谢谢!

【问题讨论】:

您的semaphore 行为未定义,因为您在锁或其他同步原语之外读取了变量。因此,在某些时候,该值可能会变得不正确。如果您打开编译器优化,这可能会更糟,因为编译器可能会假设 semaphore 没有被另一个线程修改。 @Phil1970 那么,我可以通过创建一个类并将互斥锁添加到该类并在执行信号量上下时锁定互斥锁来解决这个问题吗? 有关标准 C++ 中原子递增和递减的更多信息,请参阅 en.cppreference.com/w/cpp/atomic/atomic/operator_arith。互斥锁比原子操作有更多的开销。 Anthony WilliamsC++ Concurrency In Action 对任何想用 C++ 编写多线程代码的人来说都是一本好书。 @Phil1970 好的,我会检查并编辑相应问题的代码。 非原子操作的问题可能如下。两个线程都将内存中的值读取到寄存器中,都在寄存器中增加它们的值,并且都将值从寄存器写回内存。因此,该数字将增加 1 而不是 2。 【参考方案1】:

我找到了问题的答案。

此解决方案由 A. Tanenbaum 提出,是称为仲裁的几种解决方案之一。通过这种方法,通过引入仲裁员,例如服务员,可以保证哲学家只能选择分叉或不分叉 .为了拿起叉子,哲学家必须征得服务员的许可。服务员一次只允许一位哲学家,直到他拿起他的两把叉子。服务员可以实现为互斥体。因此,检查和更新数组的操作是原子的,并且保证了对 fork 的独占访问。 (这就是互斥锁的目的)


几个参考

https://www.cs.grinnell.edu/~weinman/courses/CSC213/2012F/labs/philosophers.html https://itsphbytes.wordpress.com/2016/09/26/dining-philosophers-problem/ https://courses.engr.illinois.edu/cs241/fa2010/ppt/25-ClassicSynch.pdf

【讨论】:

以上是关于这个解决哲学家就餐问题 (dpp) 的解决方案是如何工作的?互斥量和信号量的主要内容,如果未能解决你的问题,请参考以下文章

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

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

使用 pthread、互斥锁和条件变量解决哲学家就餐问题

哲学家就餐死锁问题及解决方案

信号量解决哲学家就餐问题(GUI动态演示)

关于哲学家就餐问题中wait()的运用,求大神解释。以下这些代码是对的还是错的?是不是解决了死锁问题