生产者与消费者模型

使用系统:Centos

简述:

在实际软件开发过程中,经常会碰到如下场景:某个模块负责产生数据,这些数据由另一个模块来负责处理(此处的模块是广义的,可以是类、函数、线程、进程等)。产生数据的模块,就形象的称为生产者,而处理数据的模块,就称为消费者。
    单单抽象出生产者和消费者,还够不上是生产者/消费者模式。该模式还需要一个缓冲区处于消费者和生产者之间,作为一个中介。生产者把数据放入缓冲区,而消费者从缓冲区拿出数据。大概的结构如下图。

生产者消费者结构

缓冲区优点:

1、解耦
假设生产者和消费者分别是两个类。如果让生产者直接调用消费者的某个方法,那么生产者对于消费者就会产生依赖(也就是耦合)。将来如果消费者的代码发生变化,可能会影响到生产者。而如果两者都依赖于某个缓冲区,两者之间不直接依赖,耦合也就相应降低了。
2、支持并发
生产者直接调⽤用消费者的某个方法,还有另一个弊端。由于函数调用是同步的(或者叫阻塞的),在消费者的方法没有返回之前,生产者只好一直等在那边。万一消费者处理数据很慢,生产者就会白白糟蹋大好时光。使用了生产者/消费者模式之后,生产者和消费者可以是两个独立的并发主体(常见并发类型有进程和线程两种)。生产者把制造出来的数据往缓冲区一丢,就可以再去生产下一个数据。基本上不用依赖消费者的处理速度。
3、支持忙闲不均
缓冲区还有另一个好处。如果制造数据的速度时快时慢,缓冲区的好处就体现出来了。当数据制造快的时候,消费者来不及处理,未处理的数据可以暂时存在缓冲区中。等生产者的制造速度慢下来,消费者再慢慢处理掉。


生产者消费者模型原则:
3种关系:
(1)互斥(生产者之间);
(2)互斥(消费者之间);
(3)互斥与同步(生产者与消费者之间);
2种角色:
(1)生产者;
(2)消费者;
1个交易场所


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

typedef struct Listnode{
    int data;
    struct Listnode *next;
}node,*pnode,**ppnode;

pnode head = NULL;
pthread_mutex_t lock =PTHREAD_MUTEX_INITIALIZER; 
pthread_cond_t cond =PTHREAD_COND_INITIALIZER; 

static pnode AllocListnode(int data)
{
    pnode tmp = (pnode)malloc(sizeof(node));
    if(tmp ==NULL)
    {
        perror("malloc");
        exit(1);
    }
    tmp->data = data;
    tmp->next = NULL;
}

static void DeleteListnode(pnode n)
{
    if(n){
        free(n);
    }
}

void InitList(ppnode node)
{
    *node = AllocListnode(0);
}

void PushFront(pnode node,int data)
{
    pnode tmp = AllocListnode(data);
    tmp->next = node->next;
    node->next = tmp;
}

void PopFront(pnode node,int *out)
{
    if(!Empty(node))
    {
    pnode tmp = node->next;
    node->next = tmp->next;
    *out = tmp->data;
    DeleteListnode(tmp);
    }
}

void ShowList(pnode node)
{
    pnode start = node->next;
    while(start)
    {
        printf("%d",start->data);
        start = start->next;
    }
    printf("\n");
}

void Destory(pnode node)
{
    int tmp = 0;
    while(!Empty(node))
    {
        PopFront(node,&tmp);
    }
    DeleteListnode(node);
}

int  Empty(pnode node)
{
    return node->next ==NULL? 1:0;
}

//消费者
 void *Consum(void *arg)
 {
     int c;
     while(1)
     {
         c = -1;
         //加锁
         pthread_mutex_lock(&lock);
         //如果为空等待
         while(Empty(head))
         {
             printf("consum begin waiting...\n");
             //条件变量等待
             pthread_cond_wait(&cond,&lock);
         }
         PopFront(head,&c);
         pthread_mutex_unlock(&lock);
         printf("consum done: %d \n",c);
        // sleep(3);
     }
 }

 //生产者
 void *Product(void *arg)
 {
     int p;
     while(1)
     {
         p = rand()%1234;
         pthread_mutex_lock(&lock);
         PushFront(head,p);
         pthread_mutex_unlock(&lock);
         //生产完了给消费者信号
         pthread_cond_signal(&cond);
         printf("product done: %d\n",p);
         sleep(3);
     }
 }

 int main()
 {
     //初始化链表
     InitList(&head);
     //创建生产者和消费者线程
     pthread_t P,C;
     pthread_create(&P,NULL,Product,NULL);
     pthread_create(&C,NULL,Consum,NULL);

     pthread_join(C,NULL);
     pthread_join(P,NULL);

     pthread_mutex_destroy(&lock);
     pthread_cond_destroy(&cond);

     //销毁链表
     Destory(head);
     return 0;
 }

消费者比生产者快:
这里写图片描述
生产者比消费者快:
这里写图片描述

基于环形队列的生产者消费者模型
这里写图片描述

环形队列是一个固定大小的临界区,如何模拟一个环呢,我们可以%上一个数组的大小。

基于环形队列的生产者与消费者模型等同于进程间通信使用信号量的方法,如上图所示生产者生产的时候需要P操作(申请格子资源),释放的时候即生产了数据之后V操作。而消费者消费的时候进行P操作(申请数据资源),释放的时候即拿走数据之后V操作。

必须满足的条件 :

消费者必须紧跟在生产者的后面(不能超过)
生产者不能给 消费者套圈
生产者和消费者不能在同一个格子
如果为满 : 消费者先跑
如果为空: 生产者先跑

代码部分:

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

#define SIZE  64
int ring[SIZE];
sem_t blank_sem;
sem_t data_sem;

void *Product(void *arg)
{
    int step = 0;
    int data = 0;
    while(1)
    {
     sem_wait(&blank_sem);
     ring[step++] = data;
     sem_post(&data_sem);
     step %= SIZE;
     printf("Product done: %d\n",data++);
     sleep(3);
    }
}
void *Comsum(void *arg)
{
    int step = 0;
    while(1)
    {
        sleep(1);
        sem_wait(&data_sem);
        int data = ring[step++];
        sem_post(&blank_sem);
        //环形
        step %= SIZE;
    printf("Comsum done :%d\n",data);
    }
}

int main()
{
    sem_init(&blank_sem,0,SIZE);
    sem_init(&data_sem,0,0);

    pthread_t P, C;
    pthread_create(&P,NULL,Product,NULL);
    pthread_create(&C,NULL,Comsum,NULL);

    pthread_join(P,NULL);
    pthread_join(C,NULL);

    sem_destroy(&blank_sem);
    sem_destroy(&data_sem);
    return 0;
}

运行结果如下:

环形队列生产消费模型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 下面是一个使用 Qt 线程池实现生产者消费者模型的简单示例: ```cpp #include <QtCore> #include <iostream> class Producer : public QObject { Q_OBJECT public: Producer(QThreadPool *pool, QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_pool(pool), m_mutex(mutex), m_cond(cond), m_data(data) { m_count = 0; } public slots: void produce() { int num = qrand() % 100; QRunnable *task = new Task(num, m_mutex, m_cond, m_data); m_pool->start(task); m_count++; std::cout << "Produced " << num << std::endl; m_cond->wakeAll(); } int count() const { return m_count; } private: QThreadPool *m_pool; QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; int m_count; }; class Consumer : public QObject { Q_OBJECT public: Consumer(QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_mutex(mutex), m_cond(cond), m_data(data) { } public slots: void consume() { m_mutex->lock(); while (m_data->isEmpty()) { m_cond->wait(m_mutex); } int num = m_data->takeFirst(); std::cout << "Consumed " << num << std::endl; m_mutex->unlock(); } private: QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; }; class Task : public QRunnable { public: Task(int num, QMutex *mutex, QWaitCondition *cond, QList<int> *data) : m_num(num), m_mutex(mutex), m_cond(cond), m_data(data) { } void run() { m_mutex->lock(); while (m_data->size() >= 10) { m_cond->wait(m_mutex); } m_data->append(m_num); m_mutex->unlock(); } private: int m_num; QMutex *m_mutex; QWaitCondition *m_cond; QList<int> *m_data; }; int main(int argc, char *argv[]) { QCoreApplication app(argc, argv); qsrand(QTime::currentTime().msec()); QThreadPool pool; pool.setMaxThreadCount(5); QMutex mutex; QWaitCondition cond; QList<int> data; Producer producer(&pool, &mutex, &cond, &data); Consumer consumer(&mutex, &cond, &data); QObject::connect(&producer, &Producer::produce, &consumer, &Consumer::consume); QTimer timer; QObject::connect(&timer, &QTimer::timeout, &producer, &Producer::produce); timer.start(1000); QTimer countTimer; QObject::connect(&countTimer, &QTimer::timeout, [&producer]() { std::cout << "Produced " << producer.count() << " items" << std::endl; }); countTimer.start(5000); return app.exec(); } ``` 在上面的示例中,`Producer` 和 `Consumer` 类继承自 `QObject`,并使用 Qt 信号和槽机制进行通信。`Producer` 类的 `produce()` ### 回答2: Qt线程池可以很好地实现生产者消费者模型生产者消费者模型主要是通过一个共享的缓冲区来实现生产者生产数据,消费者消费数据的过程。下面是一个使用Qt线程池实现生产者消费者模型的示例: 1. 定义一个数据结构,用于存储生产者消费者共享的数据。这个数据结构可以是一个队列或者一个列表。 2. 创建一个Qt线程池,并设置线程池的最大线程数。 3. 创建生产者线程和消费者线程,并将它们添加到线程池中。 4. 在生产者线程中,生产数据并将数据添加到共享的数据结构中。可以使用Qt的信号槽机制,在生产者线程中发送信号,将数据发送给消费者线程。 5. 在消费者线程中,从共享的数据结构中获取数据并进行消费。可以使用Qt的信号槽机制,在消费者线程中接收信号,并处理收到的数据。 6. 在主线程中,等待生产者线程和消费者线程完成工作,并关闭线程池。 通过使用Qt线程池,可以方便地管理多个线程,避免手动管理线程的创建和销毁,从而简化了生产者消费者模型的实现。此外,Qt的信号槽机制可以方便地实现线程间的通信,从而实现生产者消费者之间的数据传递。 ### 回答3: Qt是一个跨平台的C++应用程序开发框架,它提供了丰富的类库和工具,其中也包括线程池的实现。在Qt中,可以使用QThreadPool类来创建和管理线程池。 生产者消费者模型是一种常见的多线程编程模型,其中有一组线程作为生产者,负责生成数据,另一组线程作为消费者,负责处理这些数据。线程池可以很好地支持这种模型,并提供了以下步骤来实现: 1. 创建一个线程池对象:使用QThreadPool类创建一个线程池对象,设置最大线程数、线程闲置时间等属性。 2. 创建生产者线程:通过继承QRunnable类,实现自己的生产者线程类。在类中重写run()函数,在其中完成需要生产的数据的生成。 3. 创建消费者线程:同样通过继承QRunnable类,实现自己的消费者线程类。在类中重写run()函数,在其中完成对生产者生成的数据的处理。 4. 将任务添加到线程池:使用QThreadPool的start()函数将生产者消费者线程对象添加到线程池中,线程池会自动分配线程去运行这些任务。 5. 等待线程池完成任务:可以使用QThreadPool的waitForDone()函数来等待线程池中的所有任务完成,确保所有生产者消费者线程都执行完毕。 通过以上步骤,我们可以在Qt中实现简单的生产者消费者模型。线程池可以很好地管理线程的创建和销毁,提高线程利用率和系统的性能。同时,Qt的线程池也提供了一些其他的功能,比如任务优先级和取消线程等,可以根据实际需求来灵活调整。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值