C++知识

文章目录

1.C++map为什么线程不安全?

其实STL中的容器都是线程不安全的,如果想要线程安全的话,就得自己加锁。

C++ 标准库从 C++11 开始明确规定:多个线程并发地对同一个 std::map 对象进行读写操作,是未定义行为(UB)------除非你自己加锁(如 std::mutex)。

为什么 std::map 不自带锁?

  1. 性能优先
    标准库容器的设计哲学是零开销抽象(zero-overhead abstraction)。
    如果每个操作都加锁,即使单线程程序也会被迫承担锁的开销,这违背了 C++ 的设计原则。
  2. 灵活性更高
    用户可以根据具体场景选择锁的粒度(全局锁、分段锁、读写锁等)。
    标准库提供了 std::mutex、std::shared_mutex 等工具,让用户自己组合。

2.map大量插入会有性能问题,为什么

std::map 在"大量插入"场景下出现性能瓶颈,并不是因为它"不会用 CPU",而是由它的底层数据结构(红黑树)和接口语义共同决定的。下面把原因拆成 4 个层面,一层层展开:

① 数据结构层面:红黑树的 O(log n) 代价

插入 = 搜索插入点 + 树重平衡。

每 insert/operator[] 都要从根节点一路比较到叶子,找到插入位置;随后可能触发 旋转 + 颜色翻转,这些都是 O(log n) 的 CPU 计算。

当 n 达到百万、千万级时,log₂(n) 依旧不算小,而且每一次操作都独立走一遍完整路径,CPU 分支预测失败率高、cache miss 高。

② 内存分配层面:节点级分配带来的负担

红黑树是 "节点式容器"(node-based container),每插入一个元素就至少一次 new/malloc。

大量小对象分配 →内存碎片;分配器锁竞争(默认 glibc ptmalloc 的 global arena 锁);cache locality 极差:节点散落在堆各处,遍历时随机访问,TLB & cache 命中率低。

③ 接口语义层面:每次插入都要"唯一键"检查

std::map::insert 和 operator[] 都要 先查重,保证键唯一。就算你事先知道键不会重复,也绕不开这次查找;而哈希表(unordered_map)可以通过 reserve + emplace_hint 避免重复检查。

④ 并发层面:自带无锁机制 → 线程竞争放大问题

如果你在多线程环境用全局 mutex 保护 std::map,锁粒度是整个对象;大量插入让这把锁成为 热点,线程上下文切换/睡眠开销迅速放大(之前谈过的互斥锁 vs 自旋锁问题)。

综合症状 = 计算 + 内存 + 并发 三重放大

维度 开销来源 量级
CPU 红黑树 log n 比较 + 旋转 10⁷ 次插入时 log₂(10⁷) ≈ 23
内存 每元素一次 new + 指针开销 额外 2~3 倍内存放大
并发 全局 mutex 或分配器锁 线程越多越慢

std::map 大量插入慢,是因为 "红黑树的节点级操作 + 每次 log n 计算 + 频繁内存分配" 三者叠加;把容器换成哈希表、把一次性插入变成批量构造,或者把 map 拆掉分片,都能让性能上一个数量级。

3.set的应用场景

需求 容器
需要去重、有序遍历、范围查询是否存在 std::set / std::multiset
只去重/查询、不关心顺序 std::unordered_set(平均 O(1))
键可重复 std::multisetunordered_multiset
极高并发读写 第三方并发 set(tbb::concurrent_hash_set, folly::F14)

4.map set mutiset mutimap unordered_map unordered_set的底层实现、使用场景、优缺点

容器 底层实现 典型场景 优点 缺点
map 红黑树 键值对有序、范围查询、遍历 有序、可范围遍历、最坏O(log n) 内存碎片大、插入慢、cache差
set 红黑树 去重+有序遍历、排行榜 有序、唯一键、支持lower_bound 同map缺点
multiset 红黑树 允许重复键的有序集合 保留排序、可存重复 同map缺点
multimap 红黑树 一键多值的有序存储 一键多值、范围遍历 同map缺点
unordered_map 哈希表 键值对无序、高速增删查 平均O(1)、内存连续性好 无序、rehash开销、最坏O(n)
unordered_set 哈希表 高速去重、存在性检查 平均O(1)、简单高效 无序、rehash开销、最坏O(n)
相关推荐
学渣676562 小时前
文件传输工具rsync|rust开发环境安装|Ascend实验相关命令
开发语言·后端·rust
深思慎考2 小时前
LinuxC++项目开发日志——高并发内存池(1-定长内存池)
linux·c++
木心爱编程2 小时前
C++容器内存布局与性能优化指南
开发语言·c++·性能优化
我是渣哥2 小时前
Java String vs StringBuilder vs StringBuffer:一个性能优化的探险故事
java·开发语言·jvm·后端·算法·职场和发展·性能优化
工一木子3 小时前
深入Java并发:锁机制原理剖析与性能优化实战
java·性能优化·并发·
你我约定有三3 小时前
java--写在 try 中的创建连接
java·开发语言
咔咔咔的3 小时前
3446. 按对角线进行矩阵排序
c++
ERP老兵-冷溪虎山3 小时前
Python/JS/Go/Java同步学习(第三篇)四语言“切片“对照表: 财务“小南“纸切片术切凭证到崩溃(附源码/截图/参数表/避坑指南/老板沉默术)
java·javascript·python·golang·中医编程·四语言同步学习·职场生存指南
科技树支点3 小时前
无GC的Java创新设计思路:作用域引用式自动内存管理
java·python·go·web·编程语言·编译器