最稳妥的起点是std::mutex配std::lock_guard,它跨平台、语义清晰、异常安全;需注意临界区最小化、多锁顺序一致、避免在锁内执行阻塞操作。

std::mutex + std::lock_guard 是最稳妥的起点
绝大多数场景下,直接用 std::mutex 配合 std::lock_guard 就能解决临界区保护问题。它不依赖平台、语义清晰、异常安全——只要锁对象是静态生命周期或在线程外构造好,就不会出大问题。
常见错误现象:手写 mtx.lock() 和 mtx.unlock(),结果在循环里提前 return 或抛异常,导致锁没释放,后续线程永久阻塞。
- 必须包含
<mutex></mutex>头文件 -
std::mutex对象不能拷贝,只能移动(通常声明为全局、类成员或 static 局部变量) -
std::lock_guard构造即加锁,离开作用域自动解锁,哪怕中间 throw 也能保证释放 - 不要在
std::lock_guard作用域内调用可能阻塞或长时间运行的外部函数(比如网络 I/O),否则会拖慢其他线程
什么时候该换用 std::recursive_mutex?
只有当你明确需要「同一线程多次进入同一临界区」时才考虑它,比如递归算法访问共享容器、或封装了加锁逻辑的工具函数被反复调用。
典型误用:把 std::recursive_mutex 当成“更安全的 std::mutex”来用。它不会防止死锁,反而掩盖设计缺陷——本该拆分的临界区被强行套娃。
-
std::recursive_mutex允许同一线程重复调用lock(),但必须匹配相同次数的unlock() - 性能开销略高于
std::mutex(内部需维护调用计数) - Windows 下对应的是
CRITICAL_SECTION,但跨平台代码应优先用标准库
多个互斥量一起用,顺序错了就卡死
一旦涉及两个及以上 std::mutex,必须严格约定获取顺序,否则极大概率触发死锁。这不是概率问题,而是只要并发足够多、调度稍有变化,就会复现。
错误示例:threadA 先锁 mtx1 再锁 mtx2,而 threadB 反过来先锁 mtx2 再锁 mtx1 —— 这是教科书级死锁模板。
- 所有线程必须以完全相同的顺序获取多个锁(例如按变量地址升序、或按命名字符串字典序)
- 可用
std::scoped_lock(C++17)一次性尝试获取多个锁,避免部分加锁成功后失败回滚的麻烦 - 如果无法确定顺序,改用消息队列或无锁结构替代多锁协作
读多写少时,std::shared_mutex 能明显提升吞吐
当临界区主要是读操作(比如查配置、取缓存),只有少数写操作(比如刷新缓存),std::shared_mutex 允许多个读者并发进入,只在写时排他,比纯 std::mutex 更高效。
但它不是银弹:写操作会阻塞所有新读者,且实现开销更大;C++14 及更早版本不支持,得自己封装或退回到读写锁模拟。
- 读操作用
std::shared_lock<:shared_mutex></:shared_mutex>,写操作用std::unique_lock<:shared_mutex></:shared_mutex> - 注意:
std::shared_lock不是std::lock_guard的替代品,它不提供独占语义 - 频繁切换读/写状态反而可能比单 mutex 更慢,需实测验证收益
std::lock_guard,结果把内存分配、日志打印、甚至远程调用都锁住了——这会让并发退化成串行。真正该锁的,只是那几行读写共享变量的代码。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











