C++并发:构建线程安全的队列

正文

线程安全队列的完整的类定义,其中采用了条件变量:

#include <condition_variable>
#include <memory>
#include <mutex>
#include <queue>
template <typename T> class threadsafe_queue {private:mutable std::mutex mut;std::queue<T> data_queue;std::condition_variable data_cond;public:threadsafe_queue() {}threadsafe_queue(threadsafe_queue const &other) {std::lock_guard<std::mutex> lk(other.mut);data_queue = other.data_queue;}void push(T new_value) {std::lock_guard<std::mutex> lk(mut);data_queue.push(new_value);data_cond.notify_one();}void wait_and_pop(T &value) {std::unique_lock<std::mutex> lk(mut);data_cond.wait(lk, [this] { return !data_queue.empty(); });value = data_queue.front();data_queue.pop();}std::shared_ptr<T> wait_and_pop() {std::unique_lock<std::mutex> lk(mut);data_cond.wait(lk, [this] { return !data_queue.empty(); });std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));data_queue.pop();return res;}bool try_pop(T &value) {std::lock_guard<std::mutex> lk(mut);if (data_queue.empty())return false;value = data_queue.front();data_queue.pop();return true;}std::shared_ptr<T> try_pop() {std::lock_guard<std::mutex> lk(mut);if (data_queue.empty())return std::shared_ptr<T>();std::shared_ptr<T> res(std::make_shared<T>(data_queue.front()));data_queue.pop();return res;}bool empty() const {std::lock_guard<std::mutex> lk(mut);return data_queue.empty();}
};

这个队列的设计允许多个生产者和消费者线程安全地向队列中添加或移除元素,而无需担心数据竞争或其他并发错误。通过 std::condition_variable 的使用,消费者线程可以有效地等待直到队列中有数据可用,从而优化资源使用和线程调。

在多线程环境中,使用 mutable 关键字修饰 std::mutex 类型的成员变量是一种常见的做法,特别是在类设计中涉及到需要保护类成员不被多个线程同时修改的情况下。下面我们详细解释一下 mutable 的使用背景、意义以及为什么在 threadsafe_queue 类中应用它。

mutable的作用

mutable 修饰符用于C++中,表示即使在一个 const 成员函数中,该成员变量仍可被修改。const 成员函数承诺不修改对象的任何数据成员(不包括由 mutable 修饰的成员)。这个特性在处理需要修改类成员但又不改变对象状态的设计模式(如缓存、锁等)时非常有用。

应用于 threadsafe_queue

threadsafe_queue 类中,成员函数 empty 被声明为 const,意味着这个函数不应修改对象的任何数据成员。然而,这个函数内部需要使用 mutex 来保证线程安全性,即使它只是检查队列是否为空。由于 mutex 通常会在锁定和解锁时修改其内部状态,所以正常情况下你不能在 const 函数中进行这些操作。

为了解决这一问题,mutex 成员变量被声明为 mutable。这允许即使在 const 成员函数中,我们也可以锁定和解锁互斥量,而不违反函数的 const 性质。这样做确保了即使在多线程环境中,empty 函数执行时,队列的状态检查是线程安全的。

在构造函数中的应用

threadsafe_queue 的拷贝构造函数中,尽管传入的 other 对象是一个 const 引用,我们仍然需要从这个 const 对象中复制数据。拷贝构造函数需要访问 other 对象的 data_queue,而为了线程安全,必须先锁定 other 的互斥量。由于 mutmutable 的,即使在 const 上下文中,也能执行锁定操作。

运行结果

写一个多线程的测试程序:

void producer(threadsafe_queue<int> &queue, int start_value) {for (int i = 0; i < 5; ++i) {queue.push(start_value + i);std::this_thread::sleep_for(std::chrono::milliseconds(100)); // 模拟耗时操作}
}std::mutex print_mutex; // 保证打印有序,方便观察
void consumer(threadsafe_queue<int> &queue) {for (int i = 0; i < 5; ++i) {int value;queue.wait_and_pop(value);std::lock_guard<std::mutex> lock(print_mutex);std::cout << "Consumer " << std::this_thread::get_id()<< " popped: " << value << std::endl;}
}int main() {threadsafe_queue<int> queue;std::thread producers[3];std::thread consumers[3];// 启动生产者线程for (int i = 0; i < 3; ++i) {producers[i] = std::thread(producer, std::ref(queue),i * 10); // 每个生产者推送不同范围的数字}// 启动消费者线程for (int i = 0; i < 3; ++i) {consumers[i] = std::thread(consumer, std::ref(queue));}// 等待所有生产者线程完成for (int i = 0; i < 3; ++i) {producers[i].join();}// 等待所有消费者线程完成for (int i = 0; i < 3; ++i) {consumers[i].join();}return 0;
}

运行结果:

./main 
Consumer 0x16b333000 popped: 0
Consumer 0x16b333000 popped: 20
Consumer 0x16b3bf000 popped: 10
Consumer 0x16b44b000 popped: 1
Consumer 0x16b333000 popped: 11
Consumer 0x16b3bf000 popped: 21
Consumer 0x16b44b000 popped: 12
Consumer 0x16b333000 popped: 2
Consumer 0x16b3bf000 popped: 22
Consumer 0x16b44b000 popped: 13
Consumer 0x16b333000 popped: 3
Consumer 0x16b3bf000 popped: 23
Consumer 0x16b44b000 popped: 14
Consumer 0x16b3bf000 popped: 4
Consumer 0x16b44b000 popped: 24

这样,就实现了一个线程安全的队列。

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

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

相关文章

谈谈 HTTP 的方法

目录 方法及其作用 扩展方法 GET 和 POST 的区别 PUT 和 POST 都是给服务器发送新增资源&#xff0c;有什么区别&#xff1f; PUT和PATCH都是给服务器发送修改资源&#xff0c;有什么区别&#xff1f; 方法及其作用 HTTP/0.9 只有1种请求方法&#xff1a;GETHTTP/1.0 新增…

【Linux】线程周边001之多线程

&#x1f440;樊梓慕&#xff1a;个人主页 &#x1f3a5;个人专栏&#xff1a;《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C》《Linux》《算法》 &#x1f31d;每一个不曾起舞的日子&#xff0c;都是对生命的辜负 目录 前言 1.线程的理解 2.地址…

Github 2024-05-16 开源项目日报 Top10

根据Github Trendings的统计,今日(2024-05-16统计)共有10个项目上榜。根据开发语言中项目的数量,汇总情况如下: 开发语言项目数量Python项目4TypeScript项目2C++项目1Go项目1HTML项目1CSS项目1Cuda项目1ChatGPT/Gemini UI: 跨平台聊天应用 创建周期:433 天开发语言:TypeSc…

吴恩达深度学习笔记:优化算法 (Optimization algorithms)2.8

目录 第二门课: 改善深层神经网络&#xff1a;超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第二周&#xff1a;优化算法 (Optimization algorithms)2.8 Adam 优化算法(Adam optimization algor…

短视频批量剪辑,智能素材文案生成,多账号授权私信回复与矩阵发布素材功能合集系统,短视频矩阵助手源码搭建部署源码开源部署方案。

目录 一、短视频矩阵助手系统是什么&#xff1f; 二、短视频矩阵助手系统可以为企业解决什么问题&#xff1f; 短视频矩阵助手可以解决哪些问题&#xff1f; 三、短视频矩阵助手系统功能有哪些&#xff1f; 四、总结 一、短视频矩阵助手系统是什么&#xff1f; 短视频矩阵…

提升MySQL性能的秘密:了解表压缩功能的使用及其对性能的影响。

在MySQL数据库的使用中&#xff0c;对于字段类型设计大家可能都有一些思路和方式&#xff0c;但是针对存储方面的设计&#xff0c;在表结构设计之初可能就没考虑过&#xff0c;只有当业务发展到一定规模才意识到它所带来的问题严重性。而物理存储主要是考虑是否要启用表的压缩功…

如何把一张图片分割为网页布局

将一张图片分割为适合网页布局的步骤主要涉及使用图像编辑软件&#xff0c;如Adobe Photoshop&#xff0c;进行切片操作。以下是详细的步骤指导&#xff1a; 1. 准备图片 确保你有一张想要分割的图片&#xff0c;并且已经打开了Adobe Photoshop。 2. 选择切片工具 在工具栏…

环保科普馆如何互动化加深观众环保认知?

如今&#xff0c;多媒体技术的广泛应用&#xff0c;已经为环保、天文、生物等各类主题展厅注入了新的活力&#xff0c;在这些展馆中&#xff0c;它凭借独特的互动体验&#xff0c;以及深入浅出的教育方式&#xff0c;赢得了广大观众的热烈追捧。今天&#xff0c;我们就一同探讨…

阿里巴巴发布最新财报,营收重回增长轨道

KlipC报道&#xff1a;5月14日&#xff0c;阿里巴巴集团发布2024财年及第四财季最新财报&#xff0c;财报显示第四财季收入2218.74亿元&#xff0c;同比增长7%&#xff0c;超出市场预期。2024财年收入同比增长8%&#xff0c;达9411.68亿元。 第四财季净利润244.2亿元人民币&am…

【设计模式】JAVA Design Patterns——Aggregator Microservices(聚合器微服务模式)

&#x1f50d;目的 用户对聚合器服务进行一次调用&#xff0c;然后聚合器将调用每个相关的微服务。 &#x1f50d;解释 真实世界例子 网络市场需要有关产品及其当前库存的信息。 它调用聚合服务&#xff0c;聚合服务依次调用产品信息微服务和产品库存微服务&#xff0c;返回组合…

制作coco类型数据集

COCO格式数据集简介 COCO数据集是一个大型的、丰富的物体检测&#xff0c;分割和字幕数据集。这个数据集以scene understanding&#xff08;场景理解&#xff09;为目标&#xff0c;主要从复杂的日常场景中截取&#xff0c;图像中的目标通过精确的segmentation&#xff08;分…

道路运输驾驶员从业资格报考条件

根据《中华人民共和国道路运输条例》、交通运输部《道路运输从业人员管理规定》和上级交通运输部门的管理要求&#xff0c;参加道路运输从业资格培训时应遵守以下规定&#xff1a; 1、年龄不超过60周岁。 2、驾驶员要取得相应的机动车驾驶证&#xff08;C4以上&#xff09;。…

一步步教您轻松搭建YOLO训练环境(视频教程)

一步步教您轻松搭建YOLO训练环境 YOLO&#xff08;You Only Look Once&#xff09;是一种流行的实时目标检测算法。为了安装和部署YOLO的训练环境&#xff0c;你需要按照以下步骤进行操作&#xff1a; 一、前期准备 确定硬件要求&#xff1a;YOLO通常在具有GPU的计算机上运行…

不懂技术可以当项目经理吗?看完这篇你就知道了

项目经理作为项目的核心负责人&#xff0c;主要负责项目的规划、组织、协调和控制。 如果你不懂技术&#xff0c;但具备出色的项目管理技能&#xff0c;你仍然可以成功地管理项目。你可以通过与技术团队建立紧密的合作关系&#xff0c;明确项目需求&#xff0c;并依赖技术团队…

远程医疗系统

在远程医疗系统中&#xff0c;为了充分发挥其推动医疗科技创新的作用&#xff0c;系统可以包含以下功能点&#xff0c;并服务于不同类型的用户&#xff1a; 一、系统功能点 远程会诊系统&#xff1a; 实时视频会诊&#xff1a;医生与患者或医生与医生之间的实时视频交流。 病…

IntelliJ IDEA - 查看项目工程代码量统计

首先安装一个统计插件——Statistic 接着在左下角可以看到 Statistic Logo 插件&#xff0c;点击即可看到统计面板

SCSS详解

SCSS&#xff08;Sassy CSS&#xff09;是Sass 3引入的新语法&#xff0c;完全兼容CSS3&#xff0c;并且继承了Sass的强大功能。与原始的Sass语法不同&#xff0c;SCSS语法使用了和CSS一样的块语法&#xff0c;即使用大括号“{}”将不同的规则分开&#xff0c;使用分号“;”将具…

正则表达式和sed

一、正则表达式 主要用来匹配字符串&#xff08;命令结果&#xff0c;文本内容&#xff09;&#xff0c; 通配符匹配文件&#xff08;而且是已存在的文件&#xff09; 基本正则表达式 扩展正则表达式 1.元字符 . 匹配任意单个字符&#xff0c;可以是一个汉字 […

在C#中编写递归函数时,为了避免无限递归

在C#中编写递归函数时&#xff0c;为了避免无限递归&#xff08;也称为栈溢出&#xff09;&#xff0c;你需要确保递归调用有一个明确的终止条件。这个终止条件通常基于一个或多个参数&#xff0c;当这些参数满足某个特定条件时&#xff0c;递归就会停止并返回结果。 以下是一…

第 8 章 机器人底盘Arduino端PID控制(自学二刷笔记)

重要参考&#xff1a; 课程链接:https://www.bilibili.com/video/BV1Ci4y1L7ZZ 讲义链接:Introduction Autolabor-ROS机器人入门课程《ROS理论与实践》零基础教程 8.4.5 底盘实现_04Arduino端PID控制 上一节最后测试时&#xff0c;电机可能会出现抖动、顿挫的现象&#xff…