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

目录

  • 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

分析:

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【操作系统期末速成】 EP02 | 学习笔记(基于五道口一只鸭)

文章目录 一、前言&#x1f680;&#x1f680;&#x1f680;二、正文&#xff1a;☀️☀️☀️2.1 考点二&#xff1a;操作系统的功能及接口2.2 考点三&#xff1a;操作系统的发展及分类2.3 考点四&#xff1a;操作系统的运行环境&#xff08;重要&#xff09; 一、前言&#x…

C++输出彩色方块

1.使用方法 SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), 0xab); ———————————————————————————————————————— 0 黑色 1 蓝色 2 绿色 3 湖蓝色 4 红色 5 紫色 6 黄色 7 白色 8 灰色 9 …

【限免】线性调频信号的脉冲压缩及二维分离SAR成像算法【附MATLAB代码】

文章来源&#xff1a;微信公众号&#xff1a;EW Frontier QQ交流群&#xff1a;949444104 程序一 对线性调频信号进行仿真&#xff0c;输出其时频域的相关信息&#xff0c;并模拟回波信号&#xff0c; 对其进行脉冲压缩和加窗处理。 实验记录&#xff1a; 1.线性调频信号时…

24年hvv前夕,微步也要收费了,情报共享会在今年结束么?

一个人走的很快&#xff0c;但一群人才能走的更远。吉祥同学学安全https://mp.weixin.qq.com/s?__bizMzkwNjY1Mzc0Nw&mid2247483727&idx1&sndb05d8c1115a4539716eddd9fde4e5c9&scene21#wechat_redirect这个星球&#x1f517;里面已经沉淀了&#xff1a; 《Ja…

自闭症早期风险判别和干预新路径

谷禾健康 自闭症谱系障碍 (ASD) 是一组神经发育疾病&#xff0c;其特征是社交互动和沟通的质量障碍、兴趣受限以及重复和刻板行为。 环境因素在自闭症中发挥重要作用&#xff0c;多项研究以及谷禾队列研究文章表明肠道微生物对于自闭症的发生和发展以及存在明显的菌群和代谢物的…

智慧校园-报修管理系统总体概述

智慧校园报修管理系统是专为优化教育机构内部维修报障流程而设计的信息化解决方案&#xff0c;它通过集成现代信息技术&#xff0c;为校园设施的维护管理带来革新。该系统以用户友好和高效运作为核心&#xff0c;确保了从报修请求提交到问题解决的每一个步骤都顺畅无阻。 师生或…

Apache IoTDB 监控详解 | 分布式系统监控基础

IoTDB 分布式系统监控的基础“须知”&#xff01; 我这个环境的系统性能一直无法提升&#xff0c;能否帮我找到系统的瓶颈在哪里&#xff1f; 系统优化后&#xff0c;虽然写入性能有所提升&#xff0c;但查询延迟却增加了&#xff0c;下一步我该如何排查和优化呢&#xff1f; 请…

【折腾笔记】兰空图床使用Minio作为储存策略

前言 花了几个小时研究了一下在兰空图床中使用Minio作为存储策略,官方并没有给出太多关于minio的储存策略配置文档,我是经过反复尝试,然后根据错误日志的提示以及查阅兰空图床在GitHub上面的issues悟出来的配置方法。 因为我的兰空图床和Minio都是基于群晖的NAS设备DS423+…

[OtterCTF 2018]Bit 4 Bit

我们已经发现这个恶意软件是一个勒索软件。查找攻击者的比特币地址。** 勒索软件总喜欢把勒索标志丢在显眼的地方&#xff0c;所以搜索桌面的记录 volatility.exe -f .\OtterCTF.vmem --profileWin7SP1x64 filescan | Select-String “Desktop” 0x000000007d660500 2 0 -W-r-…

Debian/Ubuntu Linux安装OBS

先决条件 建议使用 xserver-xorg 1.18.4 或更新版本&#xff0c;以避免 OBS 中某些功能&#xff08;例如全屏投影仪&#xff09;出现潜在的性能问题。在 Linux 上使用 OBS Studio 需要 OpenGL 3.3&#xff08;或更高版本&#xff09;支持。在终端中输入以下内容来检查系统支持…

几个常见的FPGA问题之序列发生器、编码器、D触发器

几个常见的FPGA问题之序列发生器、编码器、D触发器 语言 :Verilg HDL 、VHDL EDA工具: Vivado 几个常见的FPGA问题之序列发生器、编码器、D触发器一、引言二、背景1、序列发生器(Sequence Generator)2、编码器(Encoder)3、D触发器(D Flip-Flop)二、问题及解决方案1. 序…

了解WPF控件:OpenFileDialog常用属性与用法(十六)

掌握WPF控件&#xff1a;熟练OpenFileDialog常用属性&#xff08;十六&#xff09; OpenFileDialog控件在WPF中用于需要用户指定文件路径&#xff0c;为用户提供了一个直观且易用的界面来浏览和选择本地文件系统中的文件。例如&#xff0c;当用户需要打开一个已存在的文本文件…

AD9026芯片开发实录5-ADRV9026 - FAQ

1. What information should I provide to help speed resolution of my issue?  Please provide as much detail as possible including all of the detail described in the table below 2. What are the key specifications of ADRV9026 chip?  The ADRV9026 is a 4…

Python自动化测试:web自动化测试——selenium API、unittest框架的使用

web自动化测试2 1. 设计用例的方法——selenium API1.1 基本元素定位1&#xff09;定位单个唯一元素2&#xff09;定位一组元素3&#xff09;定位多窗口/多框架4&#xff09;定位连续层级5&#xff09;定位下拉框6&#xff09;定位div框 1.2 基本操作1.3 等待1.4 浏览器操作1.5…

第三十七篇——麦克斯韦的妖:为什么要保持系统的开放性?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 如果没有详细的学习这篇文章&#xff0c;我觉得我就是被麦克斯韦妖摆弄的…

[OtterCTF 2018]Closure

既然你从内存中提取了密码&#xff0c;你能解密rick的文件吗&#xff1f; 密码是知道了&#xff0c;加密文件 &#xff1f; flag 文件&#xff1f;dump 出来 已知这个勒索软件为HiddenTear&#xff0c;直接在网上找到解密程序HiddenTearDecrypter先将加密文件的末尾多余的0去掉…

memory动态内存管理学习之weak_ptr

此头文件是动态内存管理库的一部分。std::weak_ptr 是一种智能指针&#xff0c;它持有对被 std::shared_ptr 管理的对象的非拥有性&#xff08;“弱”&#xff09;引用。在访问所引用的对象前必须先转换为 std::shared_ptr。std::weak_ptr 用来表达临时所有权的概念&#xff1a…

力扣SQL50 判断三角形 case when then end

Problem: 610. 判断三角形 Code select x,y,z,case when x y > z and x z > y and y z > x then Yeselse Noend as triangle from triangle;

Linux shell编程学习笔记59: ps 获取系统进程信息,类似于Windows系统中的tasklist 命令

0 前言 系统进程信息是电脑网络信息安全检查中的一块重要内容&#xff0c;对于使用Linux和基于Linux作为操作系统的电脑来说&#xff0c;可以使用ps命令。 1 ps命令 的功能、格式和选项说明 1.1 ps命令 的功能 Linux 中的ps&#xff08;意为&#xff1a;process status&…

<电力行业> - 《第8课:输电(一)》

1 输电环节的意义 电能的传输&#xff0c;是电力系统整体功能的重要组成环节。发电厂与电力负荷中心通常都位于不同地区。在水力、煤炭等一次能源资源条件适宜的地点建立发电厂&#xff0c;通过输电可以将电能输送到远离发电厂的负荷中心&#xff0c;使电能的开发和利用超越地…