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

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

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

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

相关文章

超简约在线生成短网址源码带后台

基于 PHP、SQLite 进行开发&#xff0c;直接上传到服务器&#xff0c;安装一下就行 源码下载&#xff1a;https://download.csdn.net/download/m0_66047725/89268253 更多资源下载&#xff1a;关注我。

TensorFlow的学习

0.基础概念 术语表&#xff1a; https://developers.google.cn/machine-learning/glossary?hlzh-cn#logits 1.快速入门 https://tensorflow.google.cn/tutorials/quickstart/beginner?hlzh-cn 2.基于Keras进行图像分类 https://tensorflow.google.cn/tutorials/keras/cl…

哈希表+DFS快速解决力扣129题:求根节点到叶节点数字之和

❤️❤️❤️ 欢迎来到我的博客。希望您能在这里找到既有价值又有趣的内容&#xff0c;和我一起探索、学习和成长。欢迎评论区畅所欲言、享受知识的乐趣&#xff01; 推荐&#xff1a;数据分析螺丝钉的首页 格物致知 终身学习 期待您的关注 导航&#xff1a; LeetCode解锁100…

roscore启动报错的解决方法【将环境变量配置于最后】

今天在启动rviz时发生一个很奇怪的报错&#xff1a; rviz: error while loading shared libraries: librviz.so: cannot open shared object file: No such file or directory 我感觉很纳闷&#xff01;再试着启动一下roscore&#xff0c;发现如下报错&#xff1a; [rosout-1…

代码随想录——填充每个节点的下一个右侧节点指针 II(Leetcode117)

题目链接 层序遍历 /* // Definition for a Node. class Node {public int val;public Node left;public Node right;public Node next;public Node() {}public Node(int _val) {val _val;}public Node(int _val, Node _left, Node _right, Node _next) {val _val;left _l…

白话机器学习7:五种降维方法的原理即Python代码实现

一、主成分分析法&#xff08;PCA&#xff09; PCA是一种常用的线性降维技术&#xff0c;它可以将高维数据投影到低维空间&#xff0c;同时保留数据中的主要变异方向。 你可以选择保留的主成分数量&#xff0c;这取决于你的具体应用和数据集。通常&#xff0c;你可以通…

flutter开发实战-JSON和序列化数据

flutter开发实战-JSON和序列化数据 大多数移动应用都需要与 web 服务器通信&#xff0c;同时在某些时候轻松地存储结构化数据。当创造需要网络连接的应用时&#xff0c;它迟早需要处理一些常见的 JSON。使用Json时候&#xff0c;可以使用json_serializable 一、引入json_anno…

安泰ATA-7015高压放大器在材料极化中的应用研究

材料极化是材料科学中一个重要的研究领域&#xff0c;它涉及到材料内部电荷和极化性质的调控和分析。高压放大器在材料极化研究中起着至关重要的作用&#xff0c;通过提供高压力和高电场条件&#xff0c;研究人员可以深入探讨材料的电子结构、相变行为以及许多其他关键性质。 材…

监控 Apache Web 服务器性能指标

Apache Web 服务器以其可靠性、灵活性和强大的功能而闻名&#xff0c;几十年来一直是互联网的支柱&#xff0c;从小型个人博客到大型电子商务平台&#xff0c;Apache 的多功能性使其能够轻松处理各种 Web 应用程序。 Apache 的 Web 服务器是如何工作的 尽管 Web 服务器涉及复…

【PB案例学习笔记】-03用户名密码校验

写在前面 通过一个个由浅入深的编程实战案例学习&#xff0c;提高编程技巧&#xff0c;以保证小伙伴们能应付公司的各种开发需求。 文章中设计到的源码&#xff0c;小凡都上传到了gitee代码仓库https://gitee.com/xiezhr/pb-project-example.git 需要源代码的小伙伴们可以自行…

企业OA办公系统开发笔记:1、搭建后端环境

文章目录 企业办公系统&#xff1a;搭建环境一、项目介绍1、介绍2、技术栈3、项目模块4、数据库 二、搭建环境1、搭建后端1.1、搭建父工程clfwzx-oa-parent1.2、搭建工具类父模块common1.3、搭建工具类common的子模块1.4、搭建实体类模块model和项目模块service-oa 2、配置依赖…

第十四届蓝桥杯大赛软件赛国赛C/C++ 大学 B 组 AB路线

//bfs 1000100010不会超时 #include<bits/stdc.h> using namespace std; #define int long long const int n1e311; int a,b,c,h[n][n][12],k[4][2]{0,1,0,-1,1,0,-1,0}; char t[n][n]; struct s {int x,y,z,w; }; signed main() {ios::sync_with_stdio(false);cin.t…

ASP.NET在线毕业论文提交系统的设计与实现

摘 要 本设计就很好的解决了上面的问题&#xff0c;它不但能实现毕业生论文的在线提交&#xff1b;还能给教师一定的权限&#xff0c;以在线的方式对自己指导的学生的论文进行审核&#xff1b;并且管理员还可以方便的将每个学生的论文信息按统一的论文排版本格式导出成word文…

Qt---TCP文件传输服务器

文件传输流程&#xff1a; 服务器端&#xff1a; serverwidget.ui serverwidget.h #ifndef SERVERWIDGET_H #define SERVERWIDGET_H#include <QWidget> #include<QTcpServer>//监听套接字 #include<QTcpSocket>//通信套接字 #include<QFile> #includ…

线上虚拟展厅需要具备哪些技术特点?

虚拟展厅需要具备三维建模与渲染技术、虚拟现实技术、交互技术、多媒体展示技术、网络传输技术、大数据分析与反馈技术、跨平台兼容性等技术特点。这些技术特点共同构成了虚拟展厅的核心竞争力&#xff0c;使其能够为用户提供逼真、生动、互动的参观体验。 虚拟展厅的技术特点主…

17.高并发场景下CAS效率的优化

文章目录 高并发场景下CAS效率的优化1.空间换时间&#xff08;LongAdder&#xff09;2.对比LongAdder和AtomicLong执行效率2.1.AtmoictLong2.2.LongAdder2.3.比对 3.LongAdder原理3.1.基类Striped64内部的三个重要成员3.2.LongAdder.add()方法3.3.LongAdder中longAccumulate()方…

【网络安全】【Frida实战案例】某图xx付费功能逆向分析(一)

文章目录 一、目标应用二、环境三、步骤1、查看布局id2、用到的Log日志类信息3、尝试hook VIP判断方法 四、总结五、相关源码 1、【网络安全】【Frida实践案例】某图xx付费功能逆向分析&#xff08;一&#xff09; 2、【网络安全】【Frida实践案例】某图xx付费功能逆向分析&…

MySQL基础--SQL优化

插入数据 insert 优化 批量插入 手动提交事务 主键顺序插入 大批量插入数据 如果一次性需要大批量插入数据&#xff0c;使用 insert 语句插入性能较低&#xff0c;此时可以使用 MySQL 数据库提供的 load 指令插入&#xff0c;操作如下&#xff1a; 主键优化 在 InnoDB 存储引擎…

QT:QML与C++交互

目录 一.介绍 二.pro文件添加模块 三.h文件 四.cpp文件 五.注册 六.调用 七.展示效果 八.代码 1.qmlandc.h 2.qmlandc.cpp 3.main.cpp 4.qml 一.介绍 在 Qt 中&#xff0c;QML 与 C 交互是非常重要的&#xff0c;因为它允许开发人员充分利用 QML 和 C 各自的优势&…

软考--试题六--策略模式(Strategy)

策略模式(strategy) 意图 定义一系列的算法&#xff0c;把它们一个个封装起来&#xff0c;并且使它们可以相互替换。此模式使得算法可以独立于使用它们的客户而变化 结构 适用性 1、许多相关的类仅仅是行为有异。“策略”提供了一种多个行为中的一个行为来配置一个类的方法…