C++并发之环形队列(ring,queue)

news2024/10/6 20:27:38

目录

  • 1 概述
  • 2 实现
  • 3 测试
  • 4 运行

1 概述

最近研究了C++11的并发编程的线程/互斥/锁/条件变量,利用互斥/锁/条件变量实现一个支持多线程并发的环形队列,队列大小通过模板参数传递。
环形队列是一个模板类,有两个模块参数,参数1是元素类型,参数2是队列大小,默认是10。入队操作如果队列满阻塞,出队操作如果队列为空则阻塞。
其类图为:
类图

2 实现

#ifndef RING_QUEUE_H
#define RING_QUEUE_H
#include <mutex>
#include <condition_variable>
template<typename T, std::size_t N = 10>
class ring_queue
{
public:
    typedef T           value_type;
    typedef std::size_t size_type;
    typedef std::size_t pos_type;
    typedef typename std::unique_lock<std::mutex> lock_type;
    ring_queue() { static_assert(N != 0); }
    ring_queue(ring_queue const&) = delete;
    ring_queue(ring_queue&& ) = delete;
    ring_queue& operator = (ring_queue const&) = delete;
    ring_queue& operator = (ring_queue &&) = delete;

    size_type spaces() const { return N; }
    bool empty() const
    {
        lock_type lock(mutex_);
        return read_pos_ == write_pos_;
    }

    size_type size() const
    {
        lock_type lock(mutex_);
        return N - space_size_;
    }

    void push(value_type const& value)
    {
        {
            lock_type lock(mutex_);
            while(!space_size_)
                write_cv_.wait(lock);

            queue_[write_pos_] = value;
            --space_size_;
            write_pos_ = next_pos(write_pos_);
        }
        read_cv_.notify_one();
    }

    void push(value_type && value)
    {
        {
            lock_type lock(mutex_);
            while(!space_size_)
                write_cv_.wait(lock);
            
            queue_[write_pos_] = std::move(value);
            --space_size_;
            write_pos_ = next_pos(write_pos_);
        }
        read_cv_.notify_one();
    }

    value_type pop()
    {
        value_type value;
        {
            lock_type lock(mutex_);
            while(N == space_size_)
                read_cv_.wait(lock);
            
            value = std::move(queue_[read_pos_]);
            ++space_size_;
            read_pos_ = next_pos(read_pos_);
        }
        write_cv_.notify_one();
        return value;
    }

private:
    pos_type next_pos(pos_type pos) { return (pos + 1) % N; }
private:
    value_type queue_[N];
    pos_type read_pos_ = 0;
    pos_type write_pos_ = 0;
    size_type space_size_ = N;
    std::mutex mutex_;
    std::condition_variable write_cv_;
    std::condition_variable read_cv_;
};
#endif

说明:

  • 实现利用了一个固定大小数组/一个读位置/一个写位置/互斥/写条件变量/读条件变量/空间大小变量。
  • 两个入队接口:
    • push(T const&) 左值入队
    • push(T &&) 左值入队
  • 一个出队接口
    • pop()

3 测试

基于cpptest的测试代码如下:

struct Function4RingQueue
{
    ring_queue<std::string, 2> queue;
    std::mutex mutex;
    int counter = 0;
    void consume1(size_t n)
    {
        std::cerr << "\n";
        for(size_t i = 0; i < n; ++i)
        {
            std::cerr << "I get a " << queue.pop() << std::endl;
            counter++;
        }
    }
    void consume2(size_t id)
    {
        std::string fruit = queue.pop();
        {
            std::unique_lock<std::mutex> lock(mutex);
            std::cerr << "\nI get a " << fruit << " in thread(" << id << ")" << std::endl;
            counter++;
        }
    }
    void product1(std::vector<std::string> & fruits)
    {
        for(auto const& fruit: fruits)
            queue.push(fruit + std::string(" pie"));
    }
    void product2(std::vector<std::string> & fruits)
    {
        for(auto const& fruit: fruits)
            queue.push(fruit);
    }
};
void RingQueueSuite::one_to_one()
{
    Function4RingQueue function;
    std::vector<std::string> fruits{"Apple", "Banana", "Pear", "Plum", "Pineapple"};
    std::thread threads[2];

    threads[0] = std::thread(&Function4RingQueue::product1, std::ref(function), std::ref(fruits));
    threads[1] = std::thread(&Function4RingQueue::consume1, std::ref(function), fruits.size());

    for(auto &thread : threads)
        thread.join();
    TEST_ASSERT_EQUALS(fruits.size(), function.counter)

    function.counter = 0;
    threads[0] = std::thread(&Function4RingQueue::product2, std::ref(function), std::ref(fruits));
    threads[1] = std::thread(&Function4RingQueue::consume1, std::ref(function), fruits.size());

    for(auto &thread : threads)
        thread.join();
    TEST_ASSERT_EQUALS(fruits.size(), function.counter)
}

void RingQueueSuite::one_to_multi()
{
    Function4RingQueue function;
    std::vector<std::string> fruits{"Apple", "Banana", "Pear", "Plum", "Pineapple"};
    std::thread product;
    std::vector<std::thread> consumes(fruits.size());

    for(size_t i = 0; i < consumes.size(); ++i)
        consumes[i] = std::thread(&Function4RingQueue::consume2, std::ref(function), i);
    product = std::thread(&Function4RingQueue::product1, std::ref(function), std::ref(fruits));
    
    product.join();
    for(auto &thread : consumes)
        thread.join();
    TEST_ASSERT_EQUALS(fruits.size(), function.counter)

    function.counter = 0;
    for(size_t i = 0; i < consumes.size(); ++i)
        consumes[i] = std::thread(&Function4RingQueue::consume2, std::ref(function), i);
    product = std::thread(&Function4RingQueue::product2, std::ref(function), std::ref(fruits));
    product.join();
    for(auto &thread : consumes)
        thread.join();
    TEST_ASSERT_EQUALS(fruits.size(), function.counter)
}
  • 函数one_to_one测试一个生成者对应一个消费者。
  • 函数one_to_multi测试一个生产者对应多个消费者。

4 运行

RingQueueSuite: 0/2
I get a Apple pie
I get a Banana pie
I get a Pear pie
I get a Plum pie
I get a Pineapple pie

I get a Apple
I get a Banana
I get a Pear
I get a Plum
I get a Pineapple
RingQueueSuite: 1/2
I get a Apple pie in thread(1)

I get a Banana pie in thread(0)

I get a Pear pie in thread(2)

I get a Plum pie in thread(4)

I get a Pineapple pie in thread(3)

I get a Apple in thread(0)

I get a Banana in thread(1)

I get a Plum in thread(3)

I get a Pear in thread(2)

I get a Pineapple in thread(4)
RingQueueSuite: 2/2, 100% correct in 0.007452 seconds
Total: 2 tests, 100% correct in 0.007452 seconds

分析:

  • 从结果看入队顺序和出队顺序是一致的。

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

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

相关文章

LeetCode 1667, 36, 199

目录 1667. 修复表中的名字题目链接表要求知识点思路代码 36. 有效的数独题目链接标签思路代码 199. 二叉树的右视图题目链接标签思路代码 1667. 修复表中的名字 题目链接 1667. 修复表中的名字 表 表Users的字段为user_id和name。 要求 编写解决方案&#xff0c;修复名字…

上位机图像处理和嵌入式模块部署(mcu 项目1:上位机编写)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 前面&#xff0c;我们说过要做一个报警器。如果只是简单做一个报警器呢&#xff0c;这个基本上没有什么难度。这里&#xff0c;我们就适当提高一下…

LLM意图识别器实践

利用 Ollama 和 LangChain 强化条件判断语句的智能提示分类 ❝ 本文译自Supercharging If-Statements With Prompt Classification Using Ollama and LangChain一文&#xff0c;以Lumos工具为例&#xff0c;讲解了博主在工程实践中&#xff0c;如何基于LangChain框架和本地LLM优…

Meta发布LLM编译器 称将改变我们的编程方式

Meta发布了Meta 大型语言模型&#xff08;LLM&#xff09;编译器&#xff0c;这是一套强大的开源模型&#xff0c;旨在优化代码并彻底改变编译器设计。这项创新有望改变开发人员优化代码的方式&#xff0c;使代码优化更快、更高效、更具成本效益。 在将大型语言模型应用于代码和…

Vue--》从零开始打造交互体验一流的电商平台(四)完结篇

今天开始使用 vue3 + ts 搭建一个电商项目平台,因为文章会将项目的每处代码的书写都会讲解到,所以本项目会分成好几篇文章进行讲解,我会在最后一篇文章中会将项目代码开源到我的github上,大家可以自行去进行下载运行,希望本文章对有帮助的朋友们能多多关注本专栏,学习更多…

20240629在NanoPi R6C开发板的预编译的Android12下使用iperf3测试网速

20240629在NanoPi R6C开发板的预编译的Android12下使用iperf3测试网速 2024/6/29 11:11 【表扬一下】友善之臂没有提供update.img的预编译固件&#xff0c;我心里一凉&#xff0c;这么多IMG文件&#xff0c;得一个一个选择呀&#xff01; 但是别人友善之臂特别急人之所急&#…

Linux部署wordpress站点

先安装宝塔面板 yum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh ed8484bec 因为wordpress需要php&#xff0c;mysql&#xff0c;apache &#xff0c;httpd环境 参考&#xff1a;Linux 安装宝塔…

Docker基础知识的掌握,相关基本命令的用法

安装docker步骤&#xff1a;https://b11et3un53m.feishu.cn/wiki/Rfocw7ctXij2RBkShcucLZbrn2d 1.docker Docker 是一种容器化平台&#xff0c;用于帮助开发者打包、发布和管理应用程序及其依赖关系。通过 Docker&#xff0c;开发者可以将应用程序及其所有依赖项打包到一个称为…

java虚拟机栈帧操作

虚拟机栈(Virtual Machine Stack)是虚拟机(如JVM、Python VM等)用来管理方法调用和执行的栈结构。它主要用于存储方法调用的相关信息,包括局部变量、操作数栈、动态链接和方法返回地址等。 java虚拟机栈操作的基本元素就是栈帧,栈帧主要包含了局部变量表、操作数栈、动态…

10分钟完成微信JSAPI支付对接过程-JAVA后端接口

引入架包 <dependency><groupId>com.github.javen205</groupId><artifactId>IJPay-WxPay</artifactId><version>${ijapy.version}</version></dependency>配置类 package com.joolun.web.config;import org.springframework.b…

【算法专题--栈】栈的压入、弹出序列 -- 高频面试题(图文详解,小白一看就懂!!)

目录 一、前言 二、题目描述 三、解题方法 &#x1f4a7;栈模拟法&#x1f4a7;-- 双指针 ⭐ 解题思路 ⭐ 案例图解 四、总结与提炼 五、共勉 一、前言 栈的压入、弹出序列 这道题&#xff0c;可以说是--栈专题--&#xff0c;最经典的一道题&#xff0c;也是在…

贪心法思想-求最大子数组和案例图解

贪心法思想 ​ 基本思想是在问题的每个决策阶段&#xff0c;都选择当前看起来最优的选择&#xff0c;即贪心地做出局部最优的决策&#xff0c;以期获得全局最优解。 ​ 正如其名字一样&#xff0c;贪心法在解决问题的策略上目光短浅&#xff0c;只根据当前已有的信息做出选择…

【FFmpeg】avformat_write_header函数

FFmpeg相关记录&#xff1a; 示例工程&#xff1a; 【FFmpeg】调用ffmpeg库实现264软编 【FFmpeg】调用ffmpeg库实现264软解 【FFmpeg】调用ffmpeg库进行RTMP推流和拉流 【FFmpeg】调用ffmpeg库进行SDL2解码后渲染 流程分析&#xff1a; 【FFmpeg】编码链路上主要函数的简单分…

VMware中安装CentOS系统

VMware中安装CentOS系统 CentOS 镜像的准备创建虚拟机Cent OS系统的安装 CentOS 镜像的准备 下载链接&#xff1a;清华园CenOS 7镜像下载 VMware的安装参考&#xff1a;VMware workstation pro 16 虚拟机的安装 创建虚拟机 1.打开VMware workstation pro 16->创建新的虚拟…

[leetcode]insert-into-a-binary-search-tree

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:TreeNode* insertIntoBST(TreeNode* root, int val) {if (root nullptr) {return new TreeNode(val);}TreeNode* pos root;while (pos ! nullptr) {if (val < pos->val) {if (pos->left nullptr…

掌握Python编程的深层技能

一、Python基础语法、变量、列表、字典等运用 1.运行python程序的两种方式 1.交互式即时得到程序的运行结果 2.脚本方式把程序写到文件里(约定俗称文件名后缀为.py),然后用python解释器解释执行其中的内容2.python程序运行的三个步骤 python3.8 C:\a\b\c.py 1.先启动python3…

揭秘循环购模式:消费即赚钱,私域电商新纪元

消费1000送2000、每天领钱、钱还可以提现&#xff0c;这样的商业模式——循环购模式&#xff0c;确实在私域电商领域引起了广泛的关注。这种模式的成功并非偶然&#xff0c;而是基于合理的返利规则和商业模式创新。下面我将为您详细解析循环购模式为何能够吸引消费者&#xff0…

记录一次——RK100键盘按键失效修复

一、背景说明: 背景&#xff1a;购买的键盘是RK的型号RK100&#xff0c;具有紧凑的外观&#xff0c;并搭配了TTC金粉轴&#xff0c;使用起来还不错&#xff0c;目前已经是第3年了。问题&#xff1a;前几个月会出现H按键失效的问题&#xff0c;但是过一段时间又会修复。最近是Q…

深入解析链表:解锁数据结构核心奥秘

一. 链表的定义 链表是一种线性数据结构&#xff0c;由一系列节点组成。每个节点包含两个部分&#xff1a; 数据域&#xff08;Data&#xff09;&#xff1a;存储节点的数据。指针域&#xff08;Pointer&#xff09;&#xff1a;存储指向下一个节点的地址。 链表的第一个节点…

一招教你用python代码给朋友写一个爱心代码

有人问我马上要跟女朋友一周年了&#xff0c;能不能用代码给他写一个爱心代码呢&#xff1f;那算你问对人了&#xff0c;来上才艺 可以使用Python的turtle模块来绘制一个爱心形状。下面是一个简单的示例代码&#xff0c;我将详细解释每一步&#xff1a; import turtle # 创建一…