std::mutex必须用RAII封装,禁用裸lock/unlock;多锁需用std::scoped_lock按地址序加锁防死锁;mutex不可拷贝,须传引用或指针;递归需求应显式用std::recursive_mutex而非默认mutex。

std::mutex 必须配合 std::lock_guard 或 std::unique_lock 使用
直接调用 mutex.lock() 和 mutex.unlock() 极易出错:忘记 unlock、异常中途跳出、提前 return 都会导致死锁。C++ 标准库不鼓励裸调用,而是要求 RAII 封装。
正确做法是让锁的生命周期绑定到作用域:
std::mutex mtx;
void safe_increment() {
std::lock_guard lock(mtx); // 构造即 lock
counter++; // 异常安全:析构自动 unlock
}
如果需要手动控制加锁时机(比如尝试加锁、延迟加锁、转移所有权),才用 std::unique_lock;日常保护临界区,std::lock_guard 更轻量、更安全。
多个 mutex 加锁顺序不一致会引发死锁
当两个线程分别以不同顺序对同一组 mutex 加锁时,比如线程 A 先 lock mtx_a 再 lock mtx_b,线程 B 反过来先 lock mtx_b 再 lock mtx_a,就可能互相等待,永久阻塞。
立即学习“C++免费学习笔记(深入)”;
解决方法只有两种:
- 始终按固定地址顺序加锁:用
std::scoped_lock(C++17 起),它自动按地址升序加锁,避免死锁 - 或手写比较逻辑,统一约定加锁顺序(如总是先 lock 地址小的那个)
std::mutex mtx_a, mtx_b;
// ✅ 推荐:用 scoped_lock 自动规避死锁
void transfer() {
std::scoped_lock lock(mtx_a, mtx_b); // 自动排序,安全
// ... 操作
}
std::mutex 不可拷贝、不可复制,只能移动(且通常不移动)
std::mutex 删除了拷贝构造函数和拷贝赋值运算符,试图 std::mutex m2 = m1; 或传值给函数会编译失败。
常见误用场景:
- 把
std::mutex成员放进容器(如std::vector<:mutex>)→ 编译不过 - 在 lambda 中按值捕获 mutex → 错误:lambda 尝试拷贝
- 传
std::mutex给线程函数作参数 → 必须用std::ref(mtx)或指针
正确做法:用指针或引用传递,或把 mutex 设为类成员并确保生命周期长于所有线程。
递归锁不是 std::mutex,要用 std::recursive_mutex
std::mutex 是非递归的:同一线程重复调用 lock() 会导致未定义行为(通常是死锁)。如果你确实需要“同一线程可重入”,必须显式使用 std::recursive_mutex。
但注意:递归锁是性能开销更大的例外,不是默认选择。90% 的同步需求里,出现“需要递归加锁”往往说明设计有问题——比如临界区划分过粗、函数职责不清晰、或没拆分好共享状态。
示例对比:
// ❌ 危险:同一线程两次 lock 同一个 std::mutex std::mutex mtx; mtx.lock(); mtx.lock(); // UB! // ✅ 如需重入,改用 recursive_mutex std::recursive_mutex rmtx; rmtx.lock(); rmtx.lock(); // OK rmtx.unlock(); rmtx.unlock();实际项目中,最常被忽略的是
std::scoped_lock 的自动死锁防护能力,以及把 mutex 当普通对象随意拷贝的编译错误——这两点卡住新手最多。











