问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

基于阻塞队列的生产者-消费者模型实现详解

创作时间:
作者:
@小白创作中心

基于阻塞队列的生产者-消费者模型实现详解

引用
CSDN
1.
https://blog.csdn.net/2301_79465388/article/details/146163027

阻塞队列(Blocking Queue)是实现生产者-消费者模型的重要数据结构,它允许生产者和消费者并发地进行数据生产和消费操作。本文将详细介绍基于阻塞队列的生产者-消费者模型的实现,包括模型的特点、代码实现以及多线程环境下的性能优化。

生产者消费者模型特点

生产者-消费者模型是一种经典的并发设计模式,其主要特点如下:

  1. 一个交易场所:以特定数据结构形式存在的一段内存空间
  2. 两种角色:生产者(生产线程)和消费者(消费线程)
  3. 三种关系
  • 生产者之间
  • 消费者之间
  • 生产者和消费者之间的关系

与阻塞队列结合后形成的特点:

  1. 同步生产和消费操作:在多线程环境下,生产者和消费者可以同时操作队列,但需要保证线程安全。
  2. 避免资源浪费:当队列满时,生产者需要等待;当队列空时,消费者需要等待,以此避免资源浪费和无效操作。
  3. 提高效率:通过设置高水位线和低水位线,适时唤醒多个生产者或消费者,提高生产和消费的效率。

阻塞队列的实现

下面是一个基于C++的阻塞队列实现:

#ifndef __BLOCK_QUEUE__
#define __BLOCK_QUEUE__

#include <cstdlib>
#include <iostream>
#include <unistd.h>
#include <pthread.h>
#include <queue>

using std::cout;
using std::endl;

template <class T>
class BlockQueue
{
private:
    bool Isfull()
    {
        return _queue.size() == _maxcap;
    }

    bool Isempty()
    {
        return _queue.empty();
    }

public:
    BlockQueue(int maxcap = 10)
        : _maxcap(maxcap), _queue(), _l_water(0), _h_water(maxcap), _call_num(maxcap / 2)
    {
        if (maxcap <= 4)
        {
            _l_water = 0x3f3f3f3f;
            _h_water = -0x3f3f3f3f;
            _call_num = 1;
        }
        else
        {
            _l_water = maxcap / 4;
            _h_water = maxcap * 3 / 4;
        }

        pthread_mutex_init(&_mutex, nullptr);
        pthread_cond_init(&_p_cond, nullptr);
        pthread_cond_init(&_c_cond, nullptr);
    }

    void Push(T &in)
    {
        pthread_mutex_lock(&_mutex);
        while (Isfull())
        {
            pthread_cond_wait(&_p_cond, &_mutex);
        }
        _queue.push(in);
        pthread_cond_signal(&_c_cond);
        if (_queue.size() >= _h_water)
        {
            cout << "call lots comsumer" << endl;
            int t = _call_num;
            while (t--)
                pthread_cond_signal(&_c_cond);
        }
        pthread_mutex_unlock(&_mutex);
    }

    void Pop(T *out)
    {
        pthread_mutex_lock(&_mutex);
        while (Isempty())
        {
            pthread_cond_wait(&_c_cond, &_mutex);
        }
        *out = _queue.front();
        _queue.pop();
        pthread_cond_signal(&_p_cond);
        if (_queue.size() <= _l_water)
        {
            cout << "call lots creator" << endl;
            int t = _call_num;
            while (t--)
                pthread_cond_signal(&_p_cond);
        }
        pthread_mutex_unlock(&_mutex);
    }

    ~BlockQueue()
    {
        pthread_mutex_destroy(&_mutex);
        pthread_cond_destroy(&_p_cond);
        pthread_cond_destroy(&_c_cond);
    }

    size_t GetSize()
    {
        return _queue.size();
    }

private:
    std::queue<int> _queue;
    int _maxcap;
    pthread_mutex_t _mutex;
    pthread_cond_t _p_cond;
    pthread_cond_t _c_cond;
    int _l_water;
    int _h_water;
    int _call_num;
};

#endif

成员变量

  • std::queue<int> _queue:标准库中的队列,用于存储数据。
  • int _maxcap:队列的最大容量。
  • pthread_mutex_t _mutex:互斥锁,用于保证生产和消费操作的互斥,避免数据竞争。
  • pthread_cond_t _p_condpthread_cond_t _c_cond:条件变量,分别用于生产者和消费者的等待和唤醒。
  • int _l_waterint _h_water:低水位线和高水位线,用于优化生产和消费的效率。
  • int _call_num:一次性唤醒的生产者或消费者数量。

构造函数

BlockQueue(int maxcap = 10)
  • 初始化队列的最大容量和相关参数。
  • 根据最大容量设置高水位线和低水位线。
  • 初始化互斥锁和条件变量。

IsfullIsempty

  • bool Isfull():判断队列是否已满。
  • bool Isempty():判断队列是否为空。

Push 函数

void Push(T &in)
  • 生产者向队列中添加数据。
  • 先获取互斥锁,进入临界区。
  • 如果队列已满,生产者在 _p_cond 条件变量上等待。
  • 向队列中添加数据后,唤醒在 _c_cond 条件变量上等待的消费者。
  • 如果队列中的数据量达到或超过高水位线,唤醒多个消费者。
  • 释放互斥锁,退出临界区。

Pop 函数

void Pop(T *out)
  • 消费者从队列中取数据。
  • 先获取互斥锁,进入临界区。
  • 如果队列为空,消费者在 _c_cond 条件变量上等待。
  • 从队列中取出数据后,唤醒在 _p_cond 条件变量上等待的生产者。
  • 如果队列中的数据量低于或等于低水位线,唤醒多个生产者。
  • 释放互斥锁,退出临界区。

析构函数

~BlockQueue()

GetSize 函数

size_t GetSize()

总结与多线程分析

这段代码通过互斥锁和条件变量实现了一个线程安全的阻塞队列,能够有效地处理生产者和消费者之间的同步问题。通过设置高水位线和低水位线,可以在数据量较多或较少时适时唤醒多个生产者或消费者,提高队列的使用效率。

对于多线程环境,这段代码可以支持多个生产者和多个消费者。虽然临界区一次只允许一个线程访问,但生产者之间和消费者之间可以并发执行,从而提高整体效率。

生产消费模型的优势

  1. 协调忙闲不均
  2. 效率高
  3. 实现生产者和消费者之间的解耦和

整个系统共用一把锁,意味着一次只能有一个线程访问临界区。多生产多消费相对于单生产单消费而言,高效体现在生产者之间和消费者之间的并发:一个生产者访问队列的时候,其他生产者也在生产数据;消费者访问队列的时候,其他消费者也在消耗数据。

本文原文来自CSDN

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号