基于C++11实现的阻塞队列(BlockQueue)

news2024/11/25 22:30:12

思路:

生产者消费者模型

 如图,多个生产者线程和多个消费者线程共享同一固定大小的缓冲区,它们的生产和消费符合以下规则:

  • 生产者不会在缓冲区满的时候继续向缓冲区放入数据,而消费者也不会在缓冲区空的时候,消耗数据
  • 当缓冲区满的时候,生产者会进入阻塞状态,当下次消费者开始消耗缓冲区的数据时,生产者才会被唤醒,开始往缓冲区中添加数据;当缓冲区空的时候,消费者也会进入阻塞状态,直到生产者往缓冲区中添加数据时才会被唤醒

所以重点为实现一个线程安全的缓冲队列:

1、首先使用STL库中的deque作为基本容器;

2、然后定义一个固定大小的容量capacity,防止无限制扩容下去导致内存耗尽;

3、之后需要两个条件变量Productor和Consumer,模拟生产者和消费者;

4、条件变量必须得有互斥量辅助,所以还需一个互斥量mutex。 

template<class T>
class BlockQueue {
public:
    explicit BlockQueue(size_t maxCapacity = 1000);
    ~BlockQueue();
private:
    std::deque<T> deq;
    size_t capacity;
    std::mutex mtx;
    std::condition_variable Consumer;
    std::condition_variable Producer;
};

主要成员函数介绍:

1、析构函数:清理队列中的所有成员,唤醒所有阻塞中的生产者、消费者线程。

template<class T>
BlockQueue<T>::~BlockQueue()
{
    {
        std::lock_guard<std::mutex> locker(mtx);
        deq.clear();
    }
    Producer.notify_all();
    Consumer.notify_all();
};

2、生产者线程应该调用的函数

(1)void BlockQueue<T>::push_back(const T& item)

template<class T>
void BlockQueue<T>::push_back(const T& item)
{
    //使用条件变量前应先对互斥量上锁,此时选择unique_lock而非lock_guard
    //因为unique_lock在上锁后允许临时解锁再加锁,而lock_guard上锁后只能在离开作用域时解锁
    std::unique_lock<std::mutex> lock(mtx);
    while (deq.size() >= capacity)
    {
        //若队列已满,则生产者线程进入阻塞状态,自动对互斥量解锁,被唤醒后又自动对互斥量加锁
        Producer.wait(lock);
    }
    deq.push_back(item);//向队列中放入数据
    Consumer.notify_one();//唤醒一个阻塞的消费者线程
}

 (2)void BlockQueue<T>::push_front(const T& item)

template<class T>
void BlockQueue<T>::push_front(const T& item)
{
    std::unique_lock<std::mutex> lock(mtx);
    while (deq.size() >= capacity)
    {
        Producer.wait(lock);
    }
    deq.push_front(item);
    Consumer.notify_one();
}

3、消费者线程应该调用的函数

(1)bool BlockQueue<T>::pop(T& item)

因为要实现线程安全的函数,所以将pop函数的接口设计为需要放入一个自己的T item作为删除数据的拷贝。 

template<class T>
bool BlockQueue<T>::pop(T& item)
{
    std::unique_lock<std::mutex> lock(mtx);
    while (deq.empty())
    {
        //若队列空,则消费者线程进入阻塞,等待被唤醒
        Consumer.wait(lock);
    }
    item = deq.front();//当作删除数据的拷贝
    deq.pop_front();
    Producer.notify_one();//唤醒一个阻塞的生产者线程
    return true;
}

(2)bool BlockQueue<T>::pop(T& item,int timeout)

为pop()函数添加第二参数timeout,设计为带计时器的pop函数。

若队列在超时时间后仍一直为空,则立刻返回false。

template<class T>
bool BlockQueue<T>::pop(T& item,int timeout)
{
    std::unique_lock<std::mutex> lock(mtx);
    while (deq.empty())
    {
        //阻塞时间超过指定的timeout,直接返回false
        if (Consumer.wait_for(lock, std::chrono::seconds(timeout)) == std::cv_status::timeout)
            return false;
    }
    item = deq.front();
    deq.pop_front();
    Producer.notify_one();
    return true;
}

 4、一些额外的成员函数

(1)判断队列为空 

template<class T>
bool BlockQueue<T>::empty()
{
    std::lock_guard<std::mutex> lock(mtx);
    return deq.empty();
}

(2)判断队列为满

template<class T>
bool BlockQueue<T>::full()
{
    std::lock_guard<std::mutex> lock(mtx);
    return deq.size()>=capacity;
}

(3)模拟队列的front函数

template<class T>
T BlockQueue<T>::front()
{
    std::lock_guard<std::mutex> lock(mtx);
    return deq.front();
}

(4)模拟队列的back函数

template<class T>
T BlockQueue<T>::back()
{
    std::lock_guard<std::mutex> lock(mtx);
    return deq.back();
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/114259.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

AQS源码解读

retrantlock&#xff1a; A、B、C3个线程&#xff0c;假设A线程lock()时候拿到了锁&#xff0c;state被A设置成了1。 static final class NonfairSync extends Sync {private static final long serialVersionUID 7316153563782823691L;/*** Performs lock. Try immediate b…

喜欢写笔记的博主为什么要使用猿如意?

&#x1f525;&#x1f525;&#x1f525;猿如意&#x1f525;&#x1f525;&#x1f525; 喜欢写笔记的博主为什么要使用猿如意&#xff1f; markdown笔记 测 评 分 享 猿如意实战测评猿如意传送门什么是猿如意&#xff1f;猿如意使用感受markdown笔记实战测评总结猿如意传…

数据结构---红包分配算法

红包分配算法错误解法二倍均值法JAVA实现线段切割法确定每一条子线段的长度JAVA实现问题如下&#xff1a; 所有人抢到的金额之和要等于红包金额&#xff0c;不能多也不能少。每个人至少抢到1分钱。要保证红包拆分的金额尽可能分布均衡&#xff0c;不要出现两极分化太严重的情况…

【C函数】函数详解

函数前言一、函数是什么二、C语言中函数的分类&#xff08;一&#xff09;库函数1.printf类2.strcpy类3.math类4.概念5.小知识6.总结&#xff08;二&#xff09;自定义函数1.概念2.函数的组成3.例子1&#xff08;求出两个数中的最大值&#xff09;4.例子2&#xff08;交换两个整…

mac释放“其他”内存空间的解决方法

官方解释Mac设备储存空间中的“其他”数据包含这不可移除的移动资源&#xff0c;例如&#xff0c;Siri 语音、字体、词典、不可移除的日志和缓存、聚焦索引以及系统数据如钥匙串和 CloudKit 数据库、系统无法删除缓存的文件等之外&#xff0c;还包含了一些无法识别的文件。当“…

ROS2 基础概念 节点

ROS2 基础概念 节点1. Nodes2. 重映射3. 环境设置3.1. ROS_DOMAIN_ID3.2. ROS_LOCALHOST_ONLY1. Nodes 每个节点应负责单个模块用途&#xff08;例如&#xff0c;一个节点用于控制车轮电机&#xff0c;一个用于控制激光测距仪等&#xff09; 可以通过话题、服务、操作或参数向…

C++-----模板

举个例子&#xff0c;如果要你交换两个数值&#xff0c;你会怎么做呢&#xff1f; ————你肯定会说&#xff0c;那就写一个Swap交换函数吧&#xff01; 没错&#xff01;Swap函数确实可以实现交换&#xff0c;但如果我想让你同时进行不能类型的数值呢&#xff0c;比如floa…

F - Permutation Distance(去绝对值数据结构)[AtCoder Beginner Contest 283]

题目如下&#xff1a; 题目链接 题解 or 思路&#xff1a; 去掉绝对值后 有 2242 \times 2 4224 中情况 虚线括起来的是需要维护的&#xff0c;其他直接枚举就行! 对于 pi<pjp_i < p_jpi​<pj​ 的情况&#xff0c;设我们维护的式子为 xxx 那我们每次枚举查找的范围…

hadoop生产调优之HDFS—集群压测

在企业中非常关心每天从 Java 后台拉取过来的数据&#xff0c;需要多久能上传到集群&#xff1f;消费者关心多久能从 HDFS 上拉取需要的数据&#xff1f; 为了搞清楚 HDFS 的读写性能&#xff0c;生产环境上非常需要对集群进行压测。 HDFS 的读写性能主要受网络和磁盘影响比较大…

【matplotlib】3-绘制统计图形

文章目录绘制统计图形1.柱状图1.1 应用场景--定性数据的分布展示1.2 绘制原理2.条形图3.堆积图3.1 堆积柱状图3.2 堆积条形图4.分块图4.1 多数据并列柱状图4.2 多数据平行条形图5.参数探索6.堆积折线图、间断条形图和阶梯图6.1 用函数stackplot()绘制堆积折线图6.2 用函数broke…

Matlab 方位角计算

文章目录 一、简介二、实现代码三、实现效果一、简介 方位角是指从某点的正北方向起顺时针旋转到某目标点方向的水平夹角,角度范围(0~360)。如下所示: 令atn= a r c t a n ( Δ Y A B / Δ X

9. SpringMvc拦截器

1. 拦截器概念和作用 拦截器&#xff08;Interceptor&#xff09;是一种动态拦截方法调用的机制&#xff0c;在SpringMVC中动态拦截控制器方法的执行作用&#xff1a; 在指定的方法调用前后执行预先设定的代码阻止原始方法的执行总结&#xff1a;增强 核心原理&#xff1a;AOP…

[CG笔记]绘制图元:三角形

学习资料是Github的一个项目Tiny renderer or how OpenGL works: software rendering in 500 lines of code 本文对应原教程的第二课的部分内容 原教程重在思路&#xff0c;主要内容是以推导为主&#xff0c;所以这里还是记录思路和为代码做注释 知乎也有人给出了中译版&…

ARM uart stdio 的移植

一、uart stdio的移植1 1. 什么是 stdio (1) #include <stdio.h> (2) stdio&#xff1a;standard input output&#xff0c;标准输入输出 (3) 标准输入输出就是操作系统定义的默认的输入和输出通道。一般在 PC 机的情况下&#xff0c;标准输入指的是键盘&#xff0c;标…

C语言——操作符详解(下)

C语言——操作符详解&#xff08;下&#xff09;一、赋值操作符二、复合赋值符三、单目操作符单目操作符介绍四、 关系操作符五、逻辑操作符六、条件操作符七、逗号表达式八、下标引用、函数调用和结构成员8.1 [ ] 下标引用操作符8.2 ( ) 函数调用操作符8.3访问一个结构的成员一…

【Linux】Linux指令串讲

大家好&#xff0c;今天要开启一个新的专题&#xff1a;Linux 今天的内容是指令还有一些基本的Linux知识补充 由于Linux的知识很难明确写出分类&#xff0c;所以目录就不会做的特别详细完全 喜欢的小伙伴点赞收藏一下不迷路哦 目录 1.目录 2.文件 3.路径 1.目录 1.创建目录…

初识Docker:(3)Docker架构

初识Docker&#xff1a;&#xff08;3&#xff09;Docker架构镜像和容器Docker和DockerHubDocker架构总结镜像和容器 镜像&#xff08;Image&#xff09;&#xff1a;Docker将应用程序及其所需的以来、函数库、环境、配置等文件打包在一起&#xff0c;成为镜像。 容器&#x…

力扣27.移除元素(双指针算法)

题目描述&#xff1a; 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考…

编译openssl支持libcurl的https访问

如果编译时不带openssl库那么无法访问https的网页&#xff0c;从网页端什么也获取不到。 在调用post请求访问翔云OCR人脸识别时无法访问&#xff0c;而使用ssl&#xff0c;需要先在系统中安装OpenSSL。 如下图安装说明&#xff1a; 所以将原先安装的libcurl库删掉&#xff0…

MySQL事务的隔离级别

&#x1f3c6;今日学习目标&#xff1a; &#x1f340;MySQL事务的隔离级别 ✅创作者&#xff1a;林在闪闪发光 ⏰预计时间&#xff1a;30分钟 &#x1f389;个人主页&#xff1a;林在闪闪发光的个人主页 &#x1f341;林在闪闪发光的个人社区&#xff0c;欢迎你的加入: 林在闪…