std::map 本身不支持并发读写,所有访问必须加锁保护;推荐封装为 threadsafemap 并用 mutable std::mutex 实现线程安全,读多写少时可用 std::shared_mutex 提升性能。

std::map 本身不支持并发读写
直接在多个线程里同时调用 find()、operator[] 或 erase(),哪怕只是“读+读”,只要中间有写操作,就可能触发迭代器失效、红黑树结构损坏或崩溃。标准库不提供任何线程安全保证——这不是 bug,是设计使然。
常见现象包括:segmentation fault、double free、std::out_of_range 意外抛出,或者看似正常但返回脏数据。
- 所有访问路径(读、写、删、
size()、empty())必须受同一把锁保护 -
mutable std::mutex是必需的:允许在const成员函数(如get())中加锁 - 绝不能返回内部
std::map的引用、指针或迭代器——锁一释放,它们立刻失效
最简可行方案:封装 + std::mutex
不需要重写底层,只需把所有 public 接口串行化。关键不是锁多细,而是“一致性”——只要某个操作依赖 map 状态(比如先 find 再 erase),就必须全程持锁。
示例核心逻辑:
template<typename k typename v>
class ThreadSafeMap {
mutable std::mutex mtx_;
std::map<k v> map_;
public:
V get(const K& key) const {
std::lock_guard<:mutex> lock(mtx_);
auto it = map_.find(key);
if (it != map_.end()) return it->second;
throw std::out_of_range("key not found");
}
void set(const K& key, const V& value) {
std::lock_guard<:mutex> lock(mtx_);
map_[key] = value;
}
void erase(const K& key) {
std::lock_guard<:mutex> lock(mtx_);
map_.erase(key);
}
};
</:mutex></:mutex></:mutex></k></typename>
- 所有成员函数都显式加锁,
mutable支持const函数内加锁 - 避免在锁内做耗时操作(如 I/O、网络调用),否则会阻塞其他线程
- 不暴露
std::map&或begin()/end()迭代器接口
读多写少时用 std::shared_mutex(C++17)
如果读操作远多于写(例如配置缓存、路由表),std::shared_mutex 允许多个线程并发读,仅写操作独占锁。实测在 8 核机器上,纯读吞吐量可提升 3–5 倍。
- 读操作用
std::shared_lock<:shared_mutex></:shared_mutex>,写操作用std::unique_lock<:shared_mutex></:shared_mutex> - GCC 8+、Clang 7+ 才完整支持;MSVC 支持但早期版本有竞态 bug
- 不要混用
std::shared_lock和std::lock_guard——类型不兼容,编译失败
遍历并删除时仍需注意迭代器安全
即使封装了锁,遍历中删除仍要遵守 std::map::erase(iterator) 返回值规则。锁只解决并发问题,不绕过迭代器失效机制。
错误写法(即使加锁也崩):
for (auto it = m.begin(); it != m.end(); ++it) {
if (should_delete(it->first)) {
m.erase(it->first); // ⚠️ it 失效,下一轮 ++it 是未定义行为
}
}
正确写法(加锁后仍需按标准方式推进):
{
std::lock_guard<:mutex> lock(mtx_);
for (auto it = map_.begin(); it != map_.end(); ) {
if (should_delete(it->first)) {
it = map_.erase(it); // ✅ 安全推进
} else {
++it;
}
}
}
</:mutex>
- 别指望锁能“修复”迭代器失效——这是容器语义层面的约束
- C++20 的
std::erase_if在加锁块内可用,语义更清晰,但底层仍是逐个erase(iterator) - 若 value 是裸指针,删除前确认它没被其他线程提前释放——锁不保 value 生命周期,只保 map 节点结构有效
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











