【问题标题】:Unable to make an Producer-Consumer instance with list in c++无法在 C++ 中使用列表创建生产者-消费者实例
【发布时间】:2021-11-04 14:38:44
【问题描述】:

伙计们。我正在学习生产者-消费者问题。我的教授给了我们一个示例代码,使用经典的int array 在两个线程之间共享资源。它按预期工作,但是,我想使用 C++ 中的std:list 类尝试它,但它没有按预期工作。消费者似乎不尊重sem_wait(&full);,所以当共享列表中没有任何内容时,它会尝试多次消费。

带有数组缓冲区的原始代码

#include    <pthread.h>
#include    <semaphore.h>
#include    <stdlib.h>
#include    <stdio.h>
#include    <unistd.h>

#define    N 2
#define    TRUE 1

int    buffer[N], in = 0, out = 0;
sem_t  empty, full, mutexC, mutexP;

void *producer(void *arg) {

   while(TRUE) {
       sleep(rand()%5);

       sem_wait(&empty);
       sem_wait(&mutexP);
    
       buffer[in] = rand() % 100;
       printf("Producing buffer[%d] = %d\n", in, buffer[in]);
       in= (in+1) % N;

       sem_post(&mutexP);
       sem_post(&full);
       
   }
}

void *consumer(void *arg) {

   while(TRUE) {
       
       sleep(rand()%5);
       sem_wait(&full);
       sem_wait(&mutexC);
       
       printf("Consuming buffer[%d] = %d\n", out, buffer[out]);
       out = (out+1) % N;
       
       sem_post(&mutexC);
       sem_post(&empty);
       
   }
}

int main(int argc, char *argv[ ]) {
    pthread_t cons, prod;

    sem_init(&mutexC, 0, 1);
    sem_init(&mutexP, 0, 1);
    sem_init(&empty, 0, N);
    sem_init(&full, 0, 0);
    
    pthread_create(&prod, NULL, producer, NULL);
    pthread_create(&cons, NULL, consumer, NULL);
    
    pthread_exit(0);
}

我的列表实现:

#include    <pthread.h>
#include    <semaphore.h>
#include    <stdlib.h>
#include    <unistd.h>
#include    <iostream>
#include    <list>

#define    TRUE 1
#define    N 2

using namespace std;

list<int> sharedList;

sem_t  empty, full, mutexC, mutexP;

void *producer(void *arg) {

   while(TRUE) {
       
       sleep(rand()%5);

       sem_wait(&empty);
       sem_wait(&mutexP);
       int prod = rand() % 100;
       cout << "producing: " << prod << endl;
       sharedList.push_back(prod);

       sem_post(&mutexP);
       sem_post(&full);
       
   }
}

void *consumer(void *arg) {

   while(TRUE) {
      
       sleep(rand()%5);

       sem_wait(&full);
       sem_wait(&mutexC);
       
       if (!sharedList.empty()) {
           cout << "consuming: ";
           cout << sharedList.front() << endl;
           sharedList.pop_front();
       } else {
           cout << "not possible to consume" << endl;
       }
       sem_post(&mutexC);
       sem_post(&empty);
   }
}

int main(int argc, char *argv[ ]) {
    pthread_t cons, prod;

    sem_init(&mutexC, 0, 1);
    sem_init(&mutexP, 0, 1);
    sem_init(&empty, 0, N);
    sem_init(&full, 0, 0);
    
    pthread_create(&prod, NULL, producer, NULL);
    pthread_create(&cons, NULL, consumer, NULL);
    
    pthread_exit(0);
}

来自我的实现的意外日志:

producing: 73
consuming: 73
not possible to consume
producing: 44
consuming: 44
producing: 9
producing: 65
consuming: 9
producing: 87
consuming: 65
consuming: producing: 29
87
not possible to consume
consuming: 29
producing: 9
producing: 60
consuming: 9
producing: 78

有人可以向我解释发生了什么吗? 提前致谢!

【问题讨论】:

    标签: c++ pthreads producer-consumer


    【解决方案1】:

    首先,请注意在这两个程序中:

    • 只有一个生产者在竞争信号量 mutexP,该信号量没有任何用处。
    • 同样,只有一个消费者曾经争夺信号量 mutexC,这也没有任何用处。

    现在,考虑一下通过使用值 2 而不是值 1 初始化信号量 empty 在您的第一个程序中的用途:我认为您会同意这样做允许生产者与消费者同时运行,领先一个位置.这没关系,因为对不同数组元素的访问不会冲突。

    但是,当您的存储是 std::list 而不是数组时,如果其中一个线程修改了它,则两个线程同时操作它是好的。有几种互补的方式来看待它,其中包括:

    • std::list 是单个对象,对其的访问必须同步,而数组只是其元素的集合,就同步要求而言,它本身并不是一个独特的对象。

    • std::list 有成员——例如保持当前列表大小——被所有操作使用,必须同步访问。

    无论您选择如何查看它,都必须确保如果其中一个线程修改了 std::list,则两个线程不能同时访问它。

    【讨论】:

    • 是的,在给出的示例中mutexPmutexC 的无用性是正确的!其目的是,一旦它开始正常工作,就可以创造更多的消费者和更多的生产者。谢谢。
    【解决方案2】:

    跟进

    虽然第二个代码确实有错误,但事实证明 macOS 确实忽略了sem_wait()。我试图用 0 初始化所有信号量,期望程序进入死锁,但实际上并没有。所以,如果在 macOS 上运行,请参考this answer

    【讨论】:

      【解决方案3】:

      rand() % 6 可以生成一些有趣的序列;所以虚假唤醒不可能消耗是可以预料的。

      除此之外,唯一令人困惑的是:

      consuming: producing: 29
      87
      not possible to consume
      

      这不是那么令人困惑; io 语句交错。如果他们没有,您会看到:

      consuming: 87
      producing: 29
      not possible to consume
      

      最后一条语句是在生产者创建之前检查条件的结果29

      【讨论】:

        猜你喜欢
        • 2012-01-12
        • 1970-01-01
        • 1970-01-01
        • 2010-10-18
        • 1970-01-01
        • 1970-01-01
        • 2012-03-12
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多