std::map 加锁

在并发环境下使用std::map,必须采取同步措施。
在并发环境下对 std::map 进行不加锁的读写操作会导致严重的线程安全问题,主要会产生以下几种问题:

⚠️ 主要风险与后果

  1. 数据竞争(Data Race)

    • 当多个线程同时修改同一个键值对时
    • 底层红黑树结构可能被破坏
    • 导致内存损坏、程序崩溃或不可预测行为
  2. 迭代器失效

    • 线程A遍历map时,线程B插入/删除元素
    • 迭代器可能失效(野指针)
    • 导致访问越界或段错误(Segmentation Fault)
  3. 脏读(Stale Read)

    • 线程A正在修改节点,线程B同时读取
    • 可能读取到部分更新的数据(半写状态)
    • 获取到不一致或无效的数据
  4. 死循环/崩溃

    • 红黑树平衡操作被并发打断
    • 树结构被破坏导致无限循环
    • 常见于插入/删除操作冲突时
  5. 性能下降

    • CPU缓存一致性协议(MESI)频繁失效
    • 大量CPU时间浪费在缓存同步上
    • 实际性能可能比加锁版本更差

🔍 具体问题场景分析

std::map<int, string> sharedMap;// 线程1
void writer() {for(int i=0; i<1000; ++i) {sharedMap[i] = to_string(i); // 并发写}
}// 线程2
void reader() {for(auto& [k,v] : sharedMap) { // 并发读cout << v << endl;}
}// 运行两个线程 -> 崩溃风险极高

✅ 解决方案(并发安全访问)

方案1:互斥锁(最常用)
#include <mutex>
std::mutex mapMutex;// 安全写入
void safeWrite(int k, string v) {std::lock_guard<std::mutex> lock(mapMutex);sharedMap[k] = v;
}// 安全读取
string safeRead(int k) {std::lock_guard<std::mutex> lock(mapMutex);return sharedMap.at(k); // 使用at避免自动插入
}
方案2:读写锁(C++17+ 读多写少场景)
#include <shared_mutex>
std::shared_mutex rwMutex;// 写操作(独占锁)
void safeWrite(int k, string v) {std::unique_lock lock(rwMutex);sharedMap[k] = v;
}// 读操作(共享锁)
string safeRead(int k) {std::shared_lock lock(rwMutex); // 多个读可并发return sharedMap.at(k);
}
方案3:线程安全容器(推荐)
#include <tbb/concurrent_hash_map.h>tbb::concurrent_hash_map<int, string> safeMap;// 写操作
void safeWrite(int k, string v) {tbb::concurrent_hash_map<int, string>::accessor acc;safeMap.insert(acc, k);acc->second = v;
}// 读操作
string safeRead(int k) {tbb::concurrent_hash_map<int, string>::const_accessor acc;if(safeMap.find(acc, k)) {return acc->second;}throw std::out_of_range("Key not found");
}
方案4:无锁编程(高级技巧)
#include <atomic>
#include <memory>// 使用atomic智能指针实现COW
std::atomic<std::shared_ptr<std::map<int, string>>> atomicMap;void safeWrite(int k, string v) {auto newMap = std::make_shared<std::map<int, string>>(*atomicMap.load());(*newMap)[k] = v;atomicMap.store(newMap); // 原子切换
}// 读操作(完全无锁)
string safeRead(int k) {auto current = atomicMap.load(); // 原子读取return current->at(k);
}

📊 性能对比(读多写少场景)

方案10读/1写100读/1写安全性
无保护崩溃崩溃
互斥锁中等
读写锁良好优秀
concurrent_hash_map优秀极佳
COW无锁极佳极佳

⚠️ 重要注意事项

  1. 迭代器安全问题

    // 危险操作(即使加锁)
    auto it = safeMap.begin();
    // 此处锁已释放,其他线程可能修改map
    // 后续使用it可能失效
    
  2. 复合操作问题

    // 非原子复合操作
    if(map.count(key)) {      // 检查value = map.at(key);  // 获取
    } // 两个操作间map可能被修改
    
  3. C++17 提取节点优化

    std::lock_guard lock(mutex);
    if(auto node = map.extract(key)) {// 安全操作节点(map已移除该元素)modify_node(node);map.insert(std::move(node));
    }
    

💡 最佳实践建议

  1. 优先使用标准库方案

    • 读写锁(C++17的std::shared_mutex
    • 对于新项目优先考虑tbb::concurrent_hash_map
  2. 避免长期持有锁

    // 反例
    {std::lock_guard lock(mutex);auto data = map.at(key); // 获取数据process(data); // 长时间处理(锁被长期占用)
    }
    
  3. 使用范围锁优化

    std::map<int, Data> snapshot;
    {std::lock_guard lock(mutex);snapshot = map; // 快速拷贝
    }
    // 在无锁状态下处理snapshot
    
  4. 考虑并发有序容器替代方案

    • folly::AtomicHashMap(Facebook)
    • ska::flat_hash_map(高性能哈希)
    • libcds::MichaelHashMap(无锁实现)

在并发环境下使用任何STL容器都必须考虑线程安全性。对于std::map,强烈建议始终使用适当的同步机制,避免未加锁的并发访问。

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

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

相关文章

学习笔记090——Ubuntu 中 UFW 防火墙的使用

文章目录1、允许特定的端口访问2、允许特定 IP 访问某个端口3、允许某个范围的端口4、查看 UFW 状态5、重新加载 UFW6、启用 UFW7、关闭 UFW1、允许特定的端口访问 # 允许 TCP 端口&#xff08;例如 80&#xff09;&#xff1a; sudo ufw allow 80/tcp# 允许 UDP 端口&#xf…

移动端 WebView 内存泄漏与性能退化问题如何排查 实战调试方法汇总

在混合 App 应用中&#xff0c;WebView 页面常承载复杂业务逻辑与交互。随着用户使用时间增长&#xff0c;特别在切换多个页面或反复打开界面后&#xff0c;常常会出现性能下降、页面卡顿、甚至白屏崩溃等现象。这通常是因为页面存在内存泄漏、事件监听未解绑或垃圾回收阻塞导致…

JSON 对象在浏览器中顺序与后端接口返回不一致的问题

一、问题描述 后端接口返回一个字典表的JSON对象&#xff0c;页面展示排序与预期排序不一致。 在浏览器调试面板Response中看到接口原始响应字符串&#xff0c;是期望顺序&#xff1a;在Preview中看到&#xff0c; key “22” 被提到最前&#xff0c;顺序发生变化&#xff1a;页…

Spring MVC数据传递全攻略

Spring MVC数据传递一、前端到后端的数据传递1. 使用 RequestParam 传递简单参数2. 使用 PathVariable传递路径参数3. 使用RequestBody传递 JSON 数据二、后端到前端的数据传递1. 使用Model或 ModelAndView传递数据到前端2. 使用HttpServletResponse直接写回数据3.使用Response…

仓库管理系统-12-前端之头部区域Header基于嵌套路由访问个人中心

文章目录 1 个人中心 1.1 DateUtils.vue(子组件) 1.2 Home.vue(父组件) 1.3 router/index.js(嵌套路由) 1.4 index.vue(路由占位符) 2 Header.vue 2.1 页面布局 2.2 toUser方法 2.3 初始加载 2.4 Header.vue 头部区域Header中有一个个人中心下拉菜单,点击个人中心选项,通过嵌…

【智能协同云图库】第七期:基于AI调用阿里云百炼大模型,实现AI图片编辑功能

摘要&#xff1a;AI 高速发展赋能传统业务&#xff0c;图库网站亦有诸多 AI 应用空间。以 AI 扩图功؜能为例&#xff0c;让我们来学习如何在项目⁠中快速接入 AI 绘图大模型。‏用户可以选择一张已上传的图片&#xff0c;‌通过 AI 扩图得到新的图片&#xff0c;希望可以帮到大…

Notepad++插件安装

方式一&#xff1a;自动安装&#xff08;有些notepad并不好用&#xff0c;推荐方式二&#xff09;工具栏-》插件-》插件管理如下点击安装后会提示&#xff0c;后端安装&#xff0c;安装成功后自动启动&#xff0c;本人使用的v8.6.4的版本&#xff0c;插件基本都无法自动安装&am…

git pull和git fetch的区别

git pull和git fetch是git版本控制系统中的两个基本命令&#xff0c;它们都用于从远程仓库更新本地仓库的信息&#xff0c;但执行的具体操作不同。git fetch:git fetch下载远程仓库最新的内容到你的本地仓库&#xff0c;但它并不自动合并或修改你当前的工作。它取回了远程仓库的…

Item35:考虑virtual函数以外的其他选择

在C++中,虚函数是实现多态的传统方式,但并非唯一选择。过度依赖虚函数可能导致派生类与基类的强耦合,或难以在运行时灵活切换行为。《Effective C++》Item35指出:应根据场景选择更合适的替代方案,包括NVI模式、函数指针、策略模式等。本文解析这些方案的原理、适用场景及实…

Vue3 状态管理新选择:Pinia 从入门到实战

一、什么是pinia? 在 Vue3 生态中&#xff0c;状态管理一直是开发者关注的核心话题。随着 Vuex 的逐步淡出&#xff0c;Pinia 作为官方推荐的状态管理库&#xff0c;凭借其简洁的 API、强大的功能和对 Vue3 特性的完美适配&#xff0c;成为了新时代的不二之选。今天我们就来深…

Unity相机控制

相机的控制无非移动和旋转&#xff0c;每种操作各3个轴6个方向&#xff0c;一共12种方式。在某些需要快速验证的项目或Demo里常常需要丝滑的控制相机调试效果。相机控制虽然不是什么高深的技术&#xff0c;但是要写的好用还是很磨人的。 锁定Z轴的旋转 一个自由的相机可以绕 …

vue2 使用liveplayer加载视频

vue2 使用liveplayer加载视频 官网: https://www.liveqing.com/docs/manuals/LivePlayer.html支持WebRTC/MP4播放;支持m3u8/HLS播放;支持HTTP-FLV/WS-FLV/RTMP播放;支持直播和点播播放;支持播放器快照截图;支持点播多清晰度播放;支持全屏或比例显示;自动检测IE浏览器兼容播放;支…

JavaScript语法树简介:AST/CST/词法/语法分析/ESTree/生成工具

AST简介 在平时的开发中&#xff0c;经常会遇到对JavaScript代码进行检查或改动的工具&#xff0c;例如ESLint会检查代码中的语法错误&#xff1b;Prettier会修改代码的格式&#xff1b;打包工具会将不同文件中的代码打包在一起等等。这些工具都对JavaScript代码本身进行了解析…

Java函数式编程之【基本数据类型流】

一、基本数据类型与基本数据的包装类 在Java编程语言中&#xff0c;int、long和double等基本数据类型都各有它们的包装类型Integer、Long和Double。 基本数据类型是Java程序语言内置的数据类型&#xff0c;可直接使用。 而包装类型则归属于普通的Java类&#xff0c;是对基本数据…

.NET Core部署服务器

1、以.NET Core5.0为例&#xff0c;在官网下载 下载 .NET 5.0 (Linux、macOS 和 Windows) | .NET 根据自己需求选择x64还是x86&#xff0c;记住关键下载完成还需要下载 Hosting Bundel &#xff0c;否则不成功 2、部署https将ssl证书放在服务器上&#xff0c;双击导入&#…

YOLO---04YOLOv3

YOLOV3 论文地址&#xff1a;&#xff1a;【https://arxiv.org/pdf/1804.02767】 YOLOV3 论文中文翻译地址&#xff1a;&#xff1a;【YOLO3论文中文版_yolo v3论文 中文版-CSDN博客】 YOLOv3 在实时性和精确性在当时都是做的比较好的&#xff0c;并在工业界得到了广泛应用 …

Qt知识点3『自定义属性的样式表失败问题』

问题1&#xff1a;自定义类中的自定义属性&#xff0c;如何通过样式表来赋值除了QT自带的属性&#xff0c;我们自定义的类中如果有自定义的静态属性&#xff0c;也可以支持样式表&#xff0c;如下 &#xff1a; Q_PROPERTY(QColor myBorderColor READ getMyBorderColor WRITE s…

RDQS_c和RDQS_t的作用及区别

&#x1f501; LPDDR5 中的 RDQS_t 和 RDQS_c — 复用机制详解 &#x1f4cc; 基本角色 引脚名 读操作&#xff08;READ&#xff09;作用 写操作&#xff08;WRITE&#xff09;作用&#xff08;当启用Link ECC&#xff09; RDQS_t Read DQS True&#xff1a;与 RDQS_c…

测试分类:详解各类测试方式与方法

前言&#xff1a;为什么要将测试进行分类呢&#xff1f;软件测试是软件生命周期中的⼀个重要环节&#xff0c;具有较高的复杂性&#xff0c;对于软件测试&#xff0c;可以从不同的角度加以分类&#xff0c;使开发者在软件开发过程中的不同层次、不同阶段对测试工作进行更好的执…

新手docker安装踩坑记录

最近在学习docker&#xff0c;安装和使用折腾了好久&#xff0c;在这里记录一下。下载# 依赖安装 sudo apt update sudo apt install -y \ca-certificates \curl \gnupg \lsb-release# 使用清华镜像源&#xff08;Ubuntu 24.04 noble&#xff09; echo \"deb [arch$(dpkg …