一个C++线程安全的栈数据结构的例子

C++ 线程安全栈数据结构示例

在多线程编程中,确保数据结构线程安全非常重要。下面是一个简单的线程安全栈数据结构的实现示例,并详细说明设计要点和容易忽略的安全点。

#include <iostream>
#include <stack>
#include <mutex>
#include <thread>
#include <condition_variable>

template <typename T>
class ThreadSafeStack {
private:
    std::stack<T> data;
    mutable std::mutex mtx; // 互斥锁,用于保护数据
    std::condition_variable cond; // 条件变量,用于线程同步

public:
    // 构造函数
    ThreadSafeStack() : data(), mtx(), cond() {}

    // 禁用拷贝构造函数和赋值操作符
    ThreadSafeStack(const ThreadSafeStack&) = delete;
    ThreadSafeStack& operator=(const ThreadSafeStack&) = delete;

    // 入栈操作
    void push(T value) {
        std::lock_guard<std::mutex> lock(mtx); // 加锁
        data.push(value);
        cond.notify_one(); // 通知等待的线程
    }

    // 出栈操作,如果栈为空则等待
    void pop(T& value) {
        std::unique_lock<std::mutex> lock(mtx); // 加锁
        cond.wait(lock, [this] { return !data.empty(); }); // 等待直到栈不为空
        value = data.top();
        data.pop();
    }

    // 尝试出栈操作,如果栈为空则返回false
    bool try_pop(T& value) {
        std::lock_guard<std::mutex> lock(mtx); // 加锁
        if (data.empty()) {
            return false;
        }
        value = data.top();
        data.pop();
        return true;
    }

    // 检查栈是否为空
    bool empty() const {
        std::lock_guard<std::mutex> lock(mtx); // 加锁
        return data.empty();
    }

    // 返回栈的大小
    size_t size() const {
        std::lock_guard<std::mutex> lock(mtx); // 加锁
        return data.size();
    }
};

// 示例使用
void push_data(ThreadSafeStack<int>& stack, int start, int end) {
    for (int i = start; i < end; ++i) {
        stack.push(i);
    }
}

void pop_data(ThreadSafeStack<int>& stack, int count) {
    for (int i = 0; i < count; ++i) {
        int value;
        stack.pop(value);
        std::cout << "Popped: " << value << std::endl;
    }
}

int main() {
    ThreadSafeStack<int> stack;

    std::thread t1(push_data, std::ref(stack), 0, 10);
    std::thread t2(push_data, std::ref(stack), 10, 20);
    std::thread t3(pop_data, std::ref(stack), 10);
    std::thread t4(pop_data, std::ref(stack), 10);

    t1.join();
    t2.join();
    t3.join();
    t4.join();

    return 0;
}


设计要点


互斥锁(Mutex):使用std::mutex来保护对共享数据的所有访问。确保在访问或修改栈数据时,始终持有互斥锁。

条件变量(Condition Variable):使用std::condition_variable来同步线程。在pop操作中,如果栈为空,线程会等待,直到有数据入栈。这样可以避免忙等待,节省CPU资源。

拷贝构造函数和赋值操作符:为了避免竞态条件和不必要的复杂性,禁用了拷贝构造函数和赋值操作符。

线程安全操作:所有对栈数据的操作(如push、pop、try_pop、empty、size)都在互斥锁的保护下进行,确保线程安全。

容易忽略的安全点


条件变量的误用:在使用条件变量时,必须确保在调用wait之前已经持有互斥锁,并且在notify之前释放互斥锁。否则,可能会导致线程死锁或错过通知。

锁的粒度:在某些情况下,锁的粒度可能会影响性能。如果锁的粒度过大(例如在每个操作中都持有锁),可能会导致性能瓶颈。在设计时,需要权衡锁的粒度和并发性能。

异常安全:在多线程环境中,异常处理变得更加复杂。确保在抛出异常时,锁能够正确释放,避免死锁。

内存模型:在多线程环境中,内存模型的选择会影响线程之间的可见性和顺序性。确保在必要时使用std::atomic或std::memory_order来控制内存顺序。

通过以上设计要点和注意事项,可以实现一个简单且线程安全的栈数据结构,确保在多线程环境下的正确性和性能。


std::unique_lock<std::mutex>和std::lock_guard<std::mutex>应用说明


在C++中,std::unique_lock和std::lock_guard都是用于管理互斥锁的RAII(Resource Acquisition Is Initialization)类。它们的主要区别在于灵活性和功能性。下面详细说明它们的应用场景和区别。

std::lock_guard


特点
简单:std::lock_guard是一个轻量级的锁管理工具,使用非常简单。
固定作用域:std::lock_guard的生命周期与其所在的代码块(通常是函数或循环)一致,当作用域结束时,锁自动释放。
不可移动:std::lock_guard不支持移动语义,因此不能转移到另一个作用域。


应用场景
简单的锁管理:当只需要在特定作用域内锁定和解锁互斥锁时,使用std::lock_guard非常方便。
自动释放锁:确保在作用域结束时自动释放锁,避免忘记解锁导致的死锁。

示例

void some_function(std::mutex& mtx) {
    std::lock_guard<std::mutex> lock(mtx); // 锁定互斥锁
    // 执行需要保护的操作
} // 作用域结束,自动释放锁


std::unique_lock


特点
灵活:std::unique_lock提供了更高级的功能,包括延迟锁定、手动解锁、转移所有权等。
可移动:std::unique_lock支持移动语义,可以转移到另一个作用域。
条件变量配合:std::unique_lock与std::condition_variable一起使用时,可以支持wait和notify操作。
应用场景
延迟锁定:当需要延迟锁定互斥锁时,例如在某些条件满足时才锁定。
手动解锁:在某些情况下,需要在作用域结束前手动解锁互斥锁。
条件变量:与std::condition_variable一起使用,实现复杂的线程同步。
锁的转移:当需要将锁的所有权从一个线程转移到另一个线程时。
示例

void some_function(std::mutex& mtx, std::condition_variable& cond, bool& ready) {
    std::unique_lock<std::mutex> lock(mtx); // 锁定互斥锁
    
    // 等待某个条件变量
    cond.wait(lock, [&ready]{ return ready; }); // 等待ready为true
    
    // 执行需要保护的操作
    
    lock.unlock(); // 手动解锁
    
    // 其他不需要保护的操作
}


总结


std::lock_guard 适用于简单的锁管理,主要用于在固定的作用域内锁定和解锁互斥锁。
std::unique_lock 适用于更复杂的场景,如延迟锁定、手动解锁、与条件变量配合使用以及锁的转移。
在选择使用哪种锁管理工具时,应根据具体需求和场景来决定。如果只需要简单的锁管理,std::lock_guard是一个很好的选择;如果需要更高级的功能,例如与条件变量配合使用,那么std::unique_lock是更合适的选择。

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

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

相关文章

本地部署大模型?看这篇就够了,Ollama 部署和实战

写在前面 前几篇&#xff0c;分享的都是如何白嫖国内外各大厂商的免费大模型服务~ 有小伙伴问&#xff0c;如果我想在本地搞个大模型玩玩&#xff0c;有什么解决方案&#xff1f; Ollama&#xff0c;它来了&#xff0c;专为在本地机器便捷部署和运行大模型而设计。 也许是目…

前端学习八股资料CSS(二)

更多详情&#xff1a;爱米的前端小笔记&#xff0c;更多前端内容&#xff0c;等你来看&#xff01;这些都是利用下班时间整理的&#xff0c;整理不易&#xff0c;大家多多&#x1f44d;&#x1f49b;➕&#x1f914;哦&#xff01;你们的支持才是我不断更新的动力&#xff01;找…

使用@react-three/fiber,@mkkellogg/gaussian-splats-3d加载.splat,.ply,.ksplat文件

前言 假设您正在现有项目中集成这些包&#xff0c;而该项目的构建工具为 Webpack 或 Vite。同时&#xff0c;您对 Three.js 和 React 有一定的了解。如果您发现有任何错误或有更好的方法&#xff0c;请随时留言。 安装 npm install three types/three react-three/fiber rea…

麻将室灯控计时计费软件下载 佳易王棋牌计时电脑收银管理系统操作教程

一、概述 【软件资源下载在文章最后】 麻将室灯控计时计费软件下载&#xff0c;棋牌计时电脑收银管理系统操作教程 核心功能‌&#xff1a; ‌计时计费‌&#xff1a;实时显示开台时间及使用时长&#xff0c;‌销售商品‌&#xff1a;商品可与桌子绑定最后结账&#xff0c;或…

【时间之外】IT人求职和创业应知【35】-RTE三进宫

目录 新闻一&#xff1a;京东工业发布11.11战报&#xff0c;多项倍增数据体现工业经济信心提升 新闻二&#xff1a;阿里云100万核算力支撑天猫双11&#xff0c;弹性计算规模刷新纪录 新闻三&#xff1a;声网CEO赵斌&#xff1a;RTE将成为生成式AI时代AI Infra的关键部分 认知…

基于BILSTM及其他RNN序列模型的人名分类器

数据集Kaggle链接 NameNationalLanguage | Kaggle 数据集分布: 第一列为人名,第二列为国家标签 代码开源地址 Kaggle代码链接 https://www.kaggle.com/code/houjijin/name-nationality-classification Gitee码云链接 人名国籍分类 Name Nation classification: using BI…

hive中windows子句的使用

概述 1&#xff0c;windows子句是对窗口的结果做更细粒度的划分 2、windows子句中有两种方式 rows &#xff1a;按照相邻的几行进行开窗 range&#xff1a;按照某个值的范围进行开窗 使用方式 (rows | range) between (UNBOUNDED | [num]) PRECEDING AND ([num] PRECEDING…

云渲染:服务器机房与物理机房两者有什么区别

云渲染选择服务器机房与物理机房两者主要区别在哪里呢&#xff1f; 服务器机房和物理机房作为云渲染的基础设施&#xff0c;各自扮演着不同的角色。 服务器机房的特点 服务器机房&#xff0c;通常指的是那些专门用于托管服务器的设施&#xff0c;它们可能位于云端&#xff0c…

零基础Java第十四期:继承与多态(二)

目录 一、继承 1.1. 继承的方式 1.2. final关键字 1.3. 继承与组合 1.4. protected关键字 二、多态 2.1. 多态的概念 2.2. 向上转型 2.3. 重写 2.4. 向下转型 2.5. 多态的优缺点 一、继承 1.1. 继承的方式 猫类可以继承动物类&#xff0c;中华田园猫类可以继承猫类…

电销系统:业绩翻倍的秘密武器

在当今竞争激烈的商业环境中&#xff0c;企业都在寻求各种方法来提升业绩。而电销系统正以其强大的功能和优势&#xff0c;成为众多企业实现业绩翻倍的有力工具。 一、高效的客户管理 电销系统能够对客户信息进行全面、系统的管理。从客户的基本资料、联系方式到历史沟通记录、…

CTFhub靶场RCE学习

靶场 eval执行 <?php if (isset($_REQUEST[cmd])) {eval($_REQUEST["cmd"]); } else {highlight_file(__FILE__); } ?> PHP代码显示&#xff0c;要求将命令赋值给cmd然后执行 先查看一下根目录文件 ?cmdsystem("ls");&#xff01;切记最后的分…

软件架构技术深入解析:AOP、系统安全架构、企业集成平台与微服务架构

目录 试题一 论面向方面的编程技术及其应用 解析 试题二 论系统安全架构设计及其应用 解析 试题三 论企业集成平台的理解与应用 解析 &#xff08;1&#xff09;通信服务 &#xff08;2&#xff09;信息集成服务 &#xff08;3&#xff09;应用集成服务 &#xff08;…

计算机网络基础:从IP地址到分层模型

计算机网络 1.计算机网络概述 概述 ​ 计算机网络是指两台或更多的计算机组成的网络&#xff0c;在同一个网络中&#xff0c;任意两台计算机都可以直接通信。互联网是网络的网络&#xff08;Internet&#xff09;&#xff0c;即把很多计算机网络连接起来&#xff0c;形成一个…

SpringCloud篇(服务提供者/消费者)(持续更新迭代)

在服务调用关系中&#xff0c;会有两个不同的角色&#xff1a; 服务提供者&#xff1a;一次业务中&#xff0c;被其它微服务调用的服务。&#xff08;提供接口给其它微服务&#xff09; 服务消费者&#xff1a;一次业务中&#xff0c;调用其它微服务的服务。&#xff08;调用…

【UML】类图及其六种关系,超详细介绍,细节满满

目录 一、概念 推荐一个画UML相关图的软件&#xff1a;ProcessOn 二、快速介绍类图 1、普通类的类图 2、抽象类的类图 3、接口的类图 三、类与类之间的关系 &#xff08;一&#xff09;、关联关系 1、单向关联 2、双向关联 3、自关联 &#xff08;二&#xff09;、聚…

HTML文件中引入jQuery的库文件

方法一&#xff1a; 1. 首先&#xff0c;在官方网站(https://jquery.com/)上下载最新版本的jQuery库文件&#xff0c;通常是一个名为jquery-x.x.x.min.js的文件。 2. 将下载的jquery-x.x.x.min.js文件保存到你的项目目录中的一个合适的文件夹中&#xff0c;比如将它保存在你的项…

使用Wireshark获取USB HID(Human Interface Device)报告描述符

使用Wireshark选择需要获取的USB进行抓取数据&#xff0c;找到设备&#xff08;host&#xff09;接收信息的数据 第二栏出现hid报告&#xff0c;右击选择复制流 将复制的内容粘贴到USB标准请求及描述符在线分析工具 - USB中文网 进行解析 以图中获取手写板的数据为例&#xff…

ReactPress与WordPress:一场内容管理系统的较量

ReactPress Github项目地址&#xff1a;https://github.com/fecommunity/reactpress WordPress官网&#xff1a;https://wordpress.org/ ReactPress与WordPress&#xff1a;一场内容管理系统的较量 在当今数字化时代&#xff0c;内容管理系统&#xff08;CMS&#xff09;已成为…

DevExpress WinForms中文教程:Data Grid - 如何绑定到实体框架数据源?

在本教程中&#xff0c;您将学习如何将DevExpress WinForms的网格控件绑定到实体框架数据源、如何使用数据注释属性来更改网格显示和管理数据的方式&#xff0c;以及如何将单元格值更改发送回数据源。 P.S&#xff1a;DevExpress WinForms拥有180组件和UI库&#xff0c;能为Wi…

使用多种机器学习调参模型进行二分类建模的全流程,代做分析辅导

使用多种机器学习调参模型进行二分类建模的全流程教程 机器学习全流程分析各个模块用到的总的参数文件 0. 分析参数文件 参数文件名称&#xff1a;total_analysis_params_demo.xlsx &#xff0c;很多分析模块都是这个总的参数文件&#xff0c;我的这个总的参数文件如果有更新…