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 pieI 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.mzph.cn/pingmian/35112.shtml

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

相关文章

[学习笔记] 禹神:一小时快速上手Electron笔记,附代码

课程地址 禹神&#xff1a;一小时快速上手Electron&#xff0c;前端Electron开发教程_哔哩哔哩_bilibili 笔记地址 https://github.com/sui5yue6/my-electron-app 进程通信 桌面软件 跨平台的桌面应用程序 chromium nodejs native api 流程模型 main主进程 .js文件 node…

Verilog HDL语法入门系列(二):Verilog的语言文字规则

目录 1 空白符和注释2 整数常量和实数常量3 整数常量和实数常量4 字符串&#xff08;string)5 格式符与转义符6 标识符(identifiers) 微信公众号获取更多FPGA相关源码&#xff1a; 1 空白符和注释 2 整数常量和实数常量 Verilog中&#xff0c;常量(literals)可是整数也可以是…

照片放大工具Topaz Gigapixel AI for Mac v7.1.2

Topaz Gigapixel AI软件是一款相当高效的PC端图像大小调整工具&#xff0c;更是一款能够为摄影师、设计师以及图像处理爱好者带来革命性体验的强大软件。它凭借先进的深度学习技术&#xff0c;打破了传统图像大小调整的限制&#xff0c;实现了真正意义上的无损放大和图像恢复。…

服务器硬件及RAID配置

目录 一、RAID磁盘阵列 1.概念 2.RAID 0 3.RAID 1 4.RAID 5 5.RAID 6 6.RAID 10 二、阵列卡 1.简介 2.缓存 三、创建 1.创建RAID 0 2.创建RAID 1 3.创建RAID 5 4.创建RAID 10 四、模拟故障 一、RAID磁盘阵列 1.概念 &#xff08;1&#xff09;是Redundant Array …

游戏服务器研究二:大世界的 scale 问题

这是一个非常陈旧的话题了&#xff0c;没什么新鲜的&#xff0c;但本人对 scale 比较感兴趣&#xff0c;所以研究得比较多。 本文不会探讨 MMO 类的网游提升单服承载人数有没有意义&#xff0c;只单纯讨论技术上如何实现。 像 moba、fps、棋牌、体育竞技等 “开房间类型的游戏…

调幅信号AM的原理与matlab实现

平台&#xff1a;matlab r2021b 本文知识内容摘自《软件无线电原理和应用》 调幅就是使载波的振幅随调制信号的变化规律而变化。用音频信号进行调幅时&#xff0c;其数学表达式可以写为: 式中&#xff0c;为调制音频信号&#xff0c;为调制指数&#xff0c;它的范围在(0&…

关于读完《额尔古纳河右岸》后的一些感受

一点废话 我本是一个喜欢读书的人&#xff0c;爱读那些有深意的书籍&#xff0c;而非现在这些《数据结构》、《LINUX 高级编程》、《编译原理》等技术性书籍。读它们时&#xff0c;我的的目的性很强&#xff0c;就是想了解它&#xff0c;思考如何运用到工作中。虽然时常也会因…

Android上编译和使用curl

1 概述 Android系统编译的时候默认是没有带curl工具的&#xff0c;但是在aosp源码中&#xff0c;却是有curl的源码包含。所以只需要编译curl&#xff0c;然后将其push到Android设备中&#xff0c;就可以使用curl命令了。 2 编译curl 这里编译curl是在整机代码环境下进行编译…

Qt添加Dialog对话框

Qt版本&#xff1a;5.12.12 1.添加【模块】 Base class&#xff1a;可以选择QDialog、QWidget、QMainWindow 会自动生成MyDialog.h和MyDialog.cpp文件以及MyDialog.ui文件&#xff0c; 2.添加代码&#xff1a; &#xff08;1&#xff09;TestDialog.h #pragma once#include…

HarmonyOS开发 - 日志打印

在程序开发过程中&#xff0c;日志输出是不可或缺的一部分。能有效的记录和分析日志数据&#xff0c;使开发人员可以更好地了解程序的运行状况、解决问题、优化性能并满足合规性要求等。 当程序出现错误或异常时&#xff0c;日志记录输出可以帮助开发人员快速定位问题发生的位置…

CppInsights: 学习C++模版的神器

CppInsights&#xff1a;深入理解C代码的利器 C是一门强大而复杂的编程语言&#xff0c;其复杂性主要体现在语言的多层次抽象和丰富的语法特性上。尽管这些特性使得C能够高效地处理复杂的任务&#xff0c;但也给开发者带来了理解和调试代码的巨大挑战。CppInsights正是在这一背…

php composer 报错

引用文章&#xff1a; Composer设置国内镜像_composer 国内源-CSDN博客 php composer.phar require --prefer-dist yiidoc/yii2-redactor "*" A connection timeout was encountered. If you intend to run Composer without connecting to the internet, run the …

【Docker】rancher 管理平台搭建

目录 1. 所有节点安装docker 2. 所有节点配置/etc/sysconfig/docker 文件修改如下配置 3. 配置证书 4. 镜像仓库导入镜像 5. 创建镜像仓库 5.1 查询上传的 image id 5.2 镜像打标签 5.3 镜像上推 6. server 节点 7. client 节点 8. 在 server 节点启动 9. 查看运行…

SHELL/作业/2024/6/25

终端输入两个数&#xff0c;判断两数是否相等&#xff0c;如果不相等&#xff0c;判断大小关系 #!/bin/basha$1b$2 if [ $a -eq $b ]then echo "ab"elif [ $a -gt $b ]thenecho "a>b"elseecho "a<b"fi2.已知网址www.hqyj.com…

算法训练营day20--235. 二叉搜索树的最近公共祖先+701.二叉搜索树中的插入操作 +450.删除二叉搜索树中的节点

一、235. 二叉搜索树的最近公共祖先 题目链接&#xff1a;https://leetcode.cn/problems/lowest-common-ancestor-of-a-binary-search-tree/ 文章讲解&#xff1a;https://programmercarl.com/0235.%E4%BA%8C%E5%8F%89%E6%90%9C%E7%B4%A2%E6%A0%91%E7%9A%84%E6%9C%80%E8%BF%91…

Linux源码阅读笔记04-实时调度类及SMP和NUMA

Linux进程分类 实时进程普通进程 如果系统中有一个实时进程并且可执行&#xff0c;调度器总是会选择他&#xff0c;除非有另外一个优先级高的实时进程。SCHED_FIFO&#xff1a;没有时间片&#xff0c;被调度器选择之后&#xff0c;可以运行任意长的时间。SCHED_RR&#xff1a;有…

Attention系列总结-粘贴自知乎

1. 梦想做个翟老师&#xff1a;阿里&#xff1a;Behavior Sequence Transformer 解读48 赞同 7 评论文章 优点:捕捉用户行为历史序列中的顺序信息。w2v也是捕捉用户序列信息的,本质差异在于啥&#xff1f; 添加图片注释&#xff0c;不超过 140 字&#xff08;可选&#xff0…

昇思25天学习打卡营第2天|onereal》

今天学习内容是了解华为昇思平台。虽然打了卡&#xff0c;但是我的jupyter里面并没有播放按钮&#xff0c;所以还是无法运行代码。反映给昇思吴彦祖小哥了&#xff0c;他说需要专家帮我解决。 我还是要自我表扬一下&#xff0c;不懂就问&#xff0c;切莫不懂装懂&#xff0c;那…

基于51单片机的RFID门禁系统-LCD12864显示

一.硬件方案 本RFID系统设计可分为硬件部分和软件部分。硬件部分以MFRC522射频识别模块为核心&#xff0c;结合主控模块STC89C52设计系统的外围硬件电路&#xff0c;实现对射频卡的控制与MCU之间的互通。软件部分采用C语言进行系统的下位机程序的开发&#xff0c;完成与IC卡之…

Windows 根据github上的环境需求,安装一个虚拟环境,安装cuda和torch

比如我们在github上看到一个关于运行环境的需求 Installation xxx系统Python 3.xxx CUDA 9.2PyTorch 1.9.0xxxxxx 最主要的就是cuda和torch&#xff0c;这两个会卡很多环境的安装。 我们重新走一遍环境安装。 首先创建一个虚拟环境 conda create -n 环境名字 python3.xxx…