如何在 C++ 中增加信号量值,以解决哲学家用餐问题

Posted

技术标签:

【中文标题】如何在 C++ 中增加信号量值,以解决哲学家用餐问题【英文标题】:How to increment the Semaphore value in c++, to solve philosophers dinning 【发布时间】:2016-08-13 06:55:23 【问题描述】:

试图通过创建一个门卫来解决哲学家就餐问题,只允许 4 位哲学家同时用餐,计划为此使用信号量,但网络上关于它们的资料有限,我不知道如何增加价值信号量一旦被发出信号。

#define INITIAL_COUNT 1 
#define MAX_COUNT 4

main()

philo.doorSemaphore = CreateSemaphore(
    NULL,           //default security attributes
    INITIAL_COUNT,  //initial count
    MAX_COUNT,  //maximum count

    NULL);

while (philo.not_dead == true)

    int num_philosophers = 5;
    for (int i = 0; i < 5; i++)
    
        philo.mythread[i] =  thread (philosophersFunction, i);      //init 5 threads calling philofunction each loop
        philo.mythread[i].join();                                   //join thread to current thread each loop
    
    sleep_for(milliseconds(500));
    system("cls");

等待()

void Philosophers::waiting(int current)


dWaitResult = WaitForSingleObject(doorSemaphore, 0L);
//waitResult = WaitForSingleObject(semaphores, 0L);

switch (dWaitResult)

case WAIT_OBJECT_0:
    p[current] = hungry;
    ReleaseSemaphore(doorSemaphore, 1, NULL);
    break;
case WAIT_TIMEOUT:
    hunger[current] ++;
    counter[current] ++;
case WAIT_FAILED :
    break;

    CloseHandle(doorSemaphore);
    

【问题讨论】:

您正在将 Windows WinAPI 线程库函数与 c++ 标准线程库混合。从标准中选择一些东西:en.cppreference.com/w/cpp/thread 【参考方案1】:

Dining Philosophers Rebooted 是使用现代 C++ 和std::threadstd::mutex 对这个经典问题的彻底处理。完整的源代码可在链接中找到。

此代码通过将每个分叉表示为std::mutex 来工作。然后诀窍是如何同时锁定两个互斥锁而不会导致死锁。 C++11/14 专门为此目的提供了一个函数:

template <class L1, class L2, class... L3>
    void lock(L1&, L2&, L3&...);

上述论文探讨了std::lock 在 2 个互斥体和 3 个互斥体情况下的几种可能实现,并确定了一种算法永远不会比任何其他算法差(而且通常要好得多)。

最佳实现(根据本文)实际上是libc++使用的算法。

这是论文中“2-D”案例的Philosopher::eat()函数:

void
Philosopher::eat()

    using Lock = std::unique_lock<std::mutex>;
    Lock first;
    Lock second;
    if (flip_coin())
    
        first = Lock(left_fork_, std::defer_lock);
        second = Lock(right_fork_, std::defer_lock);
    
    else
    
        first = Lock(right_fork_, std::defer_lock);
        second = Lock(left_fork_, std::defer_lock);
    
    auto d = get_eat_duration();
    ::lock(first, second);
    auto end = std::chrono::steady_clock::now() + d;
    while (std::chrono::steady_clock::now() < end)
        ;
    eat_time_ += d;

仅出于演示目的,Philosopher 随机选择左手和右手握住哪个叉子。解决问题不需要这种随机性。该函数可以简化为以下内容并且仍然正确:

void
Philosopher::eat()

    using Lock = std::unique_lock<std::mutex>;
    Lock first  left_fork_, std::defer_lock;
    Lock secondright_fork_, std::defer_lock;
    auto d = get_eat_duration();
    ::lock(first, second);
    auto end = std::chrono::steady_clock::now() + d;
    while (std::chrono::steady_clock::now() < end)
        ;
    eat_time_ += d;

在实际代码中,对::lock 的调用应该是std::lock,但是这段代码正在尝试std::lock 的几种实现,而没有侵入性地更改std::lib。

【讨论】:

以上是关于如何在 C++ 中增加信号量值,以解决哲学家用餐问题的主要内容,如果未能解决你的问题,请参考以下文章

使用信号量在 Java 中用餐哲学家

如何防止用餐哲学家c ++中的死锁

在 Java 中用餐哲学家

软件设计的哲学:增加复杂度的12中危险信号

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

我应该如何在 qt 中释放信号槽?