条件变量 + 互斥体 + pthreads 的 C++ 生产者消费者问题

Posted

技术标签:

【中文标题】条件变量 + 互斥体 + pthreads 的 C++ 生产者消费者问题【英文标题】:C++ Producer Consumer Problem with condition variable + mutex + pthreads 【发布时间】:2021-12-26 16:53:57 【问题描述】:

我需要用c++做生产者消费者问题,解决1个消费者和6个生产者,1个生产者和6个消费者,下面是问题的陈述。

问题 1: 想象一下,您正在一家非常繁忙的餐厅等一些朋友,而您正在看着在餐桌旁等候的工作人员将食物从厨房端到他们的餐桌上。这是经典的“生产者-消费者”问题的一个例子。服务器有限制,厨房不断生产食物。然后考虑服务器(消费者)的限制和“无限”的膳食供应由厨师(制作人)制作。

一种便于识别从而减少“生产者-消费者”问题的方法是限制消费者的数量,从而限制无限的用餐数量。 在厨房制作。因此,建议设置一个红绿灯来控制服务员所取餐点的生产顺序。

过程大概是这样的:

    创建信号量; 创建服务器和厨师线程; 尽可能多地制作餐食并记录餐食数量 正在排队; 服务器线程将一直运行,直到它设法交付所有在 表格;和 线程必须与主线程“连接”。

还考虑有 6 名厨师和 1 名服务员。如果你愿意,你可以考虑一个厨师需要 50 微秒来做一顿饭,而服务员需要 10 微秒来送餐。 饭桌上。设置服务的最大客户数量。在屏幕上打印,在执行结束时,哪个厨师最空闲和最少,以及每个厨师做了多少餐。

问题 2: 考虑到上述餐厅。现在考虑有 1 位厨师和 6 位服务员。假设厨师需要 50 微秒来制作餐点,而服务员需要 15 微秒来将餐点送到餐桌。设置服务的最大客户数量。 打印哪个服务器空闲最多和最少,以及每个服务器送了多少餐。

我设法解决了 6 个生产者和 1 个消费者,但是对于 6 个消费者和 1 个生产者它不起作用,似乎程序陷入了一些死锁。如果有人知道如何提供帮助,我将不胜感激。

#include <iostream>
#include <random>
#include <chrono>
#include <thread>
#include <mutex>
#include <deque>

//The mutex class is a synchronization primitive that can be used to protect shared data
//from being simultaneously accessed by multiple threads.
std::mutex semaforo;                       //semafoto para fazer o controle de acesso do buffer
std::condition_variable notifica;          //variavel condicional para fazer a notificação de prato consumido/consumido
std::deque<int> buffer;                    //buffer de inteiros
const unsigned int capacidade_buffer = 10; //tamanho do buffer
const unsigned int numero_pratos = 25;     //numeros de pratos a serem produzidos

void produtor()

    unsigned static int contador_pratos_produzidos = 0;
    while (contador_pratos_produzidos < numero_pratos)
    
        std::unique_lock<std::mutex> locker(semaforo);
        notifica.wait(locker, []
                       return buffer.size() < capacidade_buffer; );
        std::this_thread::sleep_for(std::chrono::microseconds(50));
        buffer.push_back(contador_pratos_produzidos);
        if (contador_pratos_produzidos < numero_pratos)
        
            contador_pratos_produzidos++;
        
        locker.unlock();
        notifica.notify_all();
    


void consumidor(int ID, std::vector<int> &consumido)

    unsigned static int contador_pratos_consumidos = 0;
    while (contador_pratos_consumidos < numero_pratos)
    
        std::unique_lock<std::mutex> locker(semaforo);
        notifica.wait(locker, []
                       return buffer.size() > 0; );
        std::this_thread::sleep_for(std::chrono::microseconds(15));
        buffer.pop_front();
        if (contador_pratos_consumidos < numero_pratos)
        
            contador_pratos_consumidos++;
            consumido[ID]++;
        
        locker.unlock();
        notifica.notify_one();
    


int main()

    //vetor para contagem do consumo de cada garcon
    std::vector<int> consumido(6, 0);

    //vetor de threads garcon(consumidores)
    std::vector<std::thread> consumidores;
    for (int k = 0; k < 6; k++)
    
        consumidores.push_back(std::thread(consumidor, k, std::ref(consumido)));
    

    //produtor/chef
    std::thread p1(produtor);

    for (auto &k : consumidores)
    
        k.join();
    
    p1.join();

    int mais_ocioso = 200, menos_ocioso = 0, mais, menos;
    for (int k = 0; k < 6; k++)
    
        std::cout << "Garcon " << k + 1 << " entregou " << consumido[k] << " pratos\n";
        if (consumido[k] > menos_ocioso)
        
            menos = k + 1;
            menos_ocioso = consumido[k];
        
        if (consumido[k] < mais_ocioso)
        
            mais = k + 1;
            mais_ocioso = consumido[k];
        
    
    std::cout << "\nO mais ocioso foi o garcon " << mais << " e o menos ocioso foi o garcon " << menos << "\n";

【问题讨论】:

【参考方案1】:

消费者和生产者函数中都存在相同的错误。我会解释其中一个,同样的错误也必须在另一个中修复。

unsigned static int contador_pratos_consumidos = 0;
while (contador_pratos_consumidos < numero_pratos)

这个static 计数器被多个执行线程访问和修改。

由多个执行线程使用的任何非原子对象都必须正确排序(仅在持有适当的互斥体时才能访问)。

如果您将注意力集中在上面两行代码上,那么很明显,该计数器是在没有任何互斥体保护的情况下访问的。一旦意识到这一点,错误就很明显了:在某些时候contador_pratos_consumidos 将恰好比numero_pratos 小一。发生这种情况时,您可以让多个执行线程同时评估 while 条件,并且所有线程都会很高兴地得出结论:这是真的。

然后多个执行线程进入while循环。一个人将成功获得互斥锁并使用“产品”,然后完成。剩余的执行线程将永远等待另一个永远不会到达的“产品”。将不再生产任何产品。他们没有汤。

同样的错误也存在于生产者中,只是错误的影响会相当微妙:最终会生产出比应有数量更多的产品。

当然,从迂腐的角度来看,所有这些都是未定义的行为,所以任何事情都可能发生,但这些是这种未定义行为的典型、常见的后果。必须修复这两个错误才能使该算法正常工作。

【讨论】:

以上是关于条件变量 + 互斥体 + pthreads 的 C++ 生产者消费者问题的主要内容,如果未能解决你的问题,请参考以下文章

Linux系统编程-(pthread)线程通信(条件变量)

Linux C 多线程编程之互斥锁与条件变量实例详解

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

pthreads等待和信号疑问linux

C/C++ 和其他语言中的条件变量使用模式

互斥锁和条件变量(pthread)相关函数