大并发下应减少锁使用,优先用std::atomic处理单变量操作,读多写少时用std::shared_mutex,多锁需用std::lock避免死锁,粗粒度锁应分片或改用无锁结构。

大并发下频繁加锁不是“锁得不够快”,而是“锁得太多、太重、太粗”。核心解法是:能不用锁就不用,必须用时选对原语,临界区越小越好。
std::atomic 能搞定的,别碰 std::mutex
计数器、开关标志、指针替换这类单变量操作,std::atomic 是默认选项。它由硬件指令(如 x86 的 lock xadd)保障原子性,无上下文切换、无系统调用开销。
- 错误写法:
int counter = 0;+std::mutex mtx;+ 每次mtx.lock(); ++counter; mtx.unlock(); - 正确写法:
std::atomic<int> counter{0};</int>+ 直接counter.fetch_add(1, std::memory_order_relaxed); - 注意:
std::memory_order_relaxed适用于纯计数;若涉及状态同步(如“写完数据再置 flag”),需升级为std::memory_order_release/std::memory_order_acquire
读多写少?换用 std::shared_mutex + std::shared_lock
当缓存、配置表、路由映射等结构被大量读取、极少更新时,std::mutex 会把所有读者串行化,吞吐直接腰斩。改用读写锁可让读并发、写独占。
-
std::shared_mutex是 C++17 标准组件,无需第三方依赖 - 读操作用
std::shared_lock<:shared_mutex></:shared_mutex>,允许多个线程同时进入 - 写操作用
std::unique_lock<:shared_mutex></:shared_mutex>,自动排他 - 性能差异明显:实测读密集场景下,
std::shared_mutex平均延迟比std::mutex低一半以上(42ns vs 85ns)
多个锁一起拿?必须用 std::lock,别手写顺序
需要同时保护多个资源(如转账时锁两个账户)时,手写 mtx1.lock(); mtx2.lock(); 极易因线程调度导致死锁。这不是概率问题,是必然风险。
- 永远用
std::lock(mtx1, mtx2)原子获取 —— 它内部采用试探+回退策略,保证要么全得,要么全不得 - 配合
std::adopt_lock构造std::lock_guard,避免重复加锁 - 切忌按地址比较排序后手动加锁:地址可能变化(ASLR、动态分配),且逻辑分散难维护
锁粒度太粗?拆、分、隔离
一把锁护整个 std::unordered_map,等于让所有线程排队查不同 key。实际只需隔离冲突 key 的操作。
- 分片锁(sharding):把大容器拆成 N 个小容器 + N 把锁,key 哈希后映射到对应分片
- 读写分离:写操作走队列异步批量处理,读走只读快照(如
std::shared_ptr交换) - 无锁结构替代:高频生产消费场景,优先考虑
moodycamel::ConcurrentQueue或自研 CAS 队列,而非加锁std::queue
真正棘手的从来不是“怎么加锁”,而是“哪里其实根本不需要锁”。每次加锁前,先问一句:这个变量是否真的会被多线程同时修改?它的生命周期和访问模式是否允许用原子操作或数据副本绕过同步?
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











