c++oding="utf-8" ?>
std::mutex必须配合raii机制使用,推荐std::lock_guard;多锁需统一顺序或用std::lock避免死锁;类中mutex成员须显式初始化;非递归mutex重复加锁会抛resource_deadlock_would_occur异常。

std::mutex 必须配合 std::lock_guard 或 std::unique_lock 使用
直接调用 mutex.lock() 和 mutex.unlock() 极易出错——比如异常中途抛出,unlock() 就永远不会执行,导致死锁。C++ 标准库不鼓励手动配对加解锁,而是依赖 RAII 自动管理生命周期。
最常用的是 std::lock_guard:构造时加锁,析构时自动解锁,且不可移动、不可复制,语义清晰安全。
-
std::lock_guard<:mutex></:mutex>是最轻量、最推荐的默认选择,适合简单临界区 - 需要延迟加锁、尝试加锁、或转移锁所有权时,才用
std::unique_lock - 绝不要在函数内局部声明
std::mutex后直接lock()/unlock()—— 这是典型隐患点
多个 mutex 加锁顺序不一致会引发死锁
当一段逻辑需同时持有两个及以上互斥锁时(例如交换两个容器内容),若线程 A 先锁 mtx_a 再锁 mtx_b,而线程 B 反过来先锁 mtx_b 再锁 mtx_a,就可能互相等待,永久卡住。
解决方式不是“加 try_lock 循环重试”,而是统一加锁顺序:
- 按地址大小排序:
std::lock(mtx_a, mtx_b)是标准方案,它内部使用无死锁算法批量加锁 - 配合
std::lock_guard时,要用std::defer_lock参数避免立即加锁:std::lock(mtx_a, mtx_b); std::lock_guard<:mutex> guard_a(mtx_a, std::defer_lock); std::lock_guard<:mutex> guard_b(mtx_b, std::defer_lock);</:mutex></:mutex>
- 更稳妥的做法是设计成「单 mutex 管理多资源」,或用读写锁(
std::shared_mutex)降低粒度
std::mutex 不可复制、不可移动,成员变量需显式初始化
类中包含 std::mutex 成员时,编译器会自动删除默认拷贝/移动构造函数和赋值操作符。如果你写了自定义构造函数但忘了初始化 mutex,会导致未定义行为。
正确写法是用成员初始化列表显式构造:
class Counter {
std::mutex mtx_;
int value_ = 0;
public:
Counter() : mtx_{} {} // 必须显式初始化,不能留空
void increment() {
std::lock_guard<:mutex> lk(mtx_);
++value_;
}
};</:mutex>
- 不能写
std::mutex mtx_ = std::mutex{};(虽能编译,但冗余且易误导) - 不能把
std::mutex放进std::vector或其他要求可移动/可复制的容器里 - 跨线程传递对象时,确保 mutex 所在对象的生命周期长于所有使用它的线程
调试时遇到 resource_deadlock_would_occur 说明已触发死锁检测
某些平台(如 macOS 的 libc++ 或启用 libstdc++ 的调试模式)会在重复对同一线程加锁时抛出 std::system_error,错误码为 resource_deadlock_would_occur。这不是 warning,是运行时异常,程序会 terminate。
这通常意味着你误用了递归锁——而 std::mutex 是非递归的。解决方案只有两个:
- 确认没在同一线程中对同一个
std::mutex多次调用lock() - 真需要递归语义,改用
std::recursive_mutex,但要清楚它性能更低、且掩盖了设计问题 - 用工具辅助排查:Linux 下可用
helgrind,macOS 下可用Thread Sanitizer(-fsanitize=thread)
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











