C++标准库中可重入互斥锁是std::recursive_mutex;它支持同一线程多次加锁,内部维护持有计数和线程ID,而std::mutex不可重入,重复lock会导致未定义行为。

可重入互斥锁在 C++ 标准库中不存在
标准 C++ 的 std::mutex 和 std::recursive_mutex 都是线程安全的,但只有 std::recursive_mutex 支持同一线程多次加锁——它就是 C++ 标准提供的「可重入互斥锁」。很多人误以为 std::mutex 可重入,实际不是:同一线程重复调用 lock() 会导致未定义行为(通常是死锁或程序终止)。
-
std::mutex:不可重入,轻量,性能略优,适用于明确无递归调用场景 -
std::recursive_mutex:可重入,内部维护持有计数和持有线程 ID,开销稍大,但语义安全 - POSIX 的
PTHREAD_MUTEX_RECURSIVE或 Windows 的CRITICAL_SECTION也属可重入,但 C++ 应优先用标准类型
std::recursive_mutex 的正确用法与常见错误
用错的关键往往不在声明,而在生命周期管理和异常安全。它不像 std::mutex 那样“容易 forgiving”,一旦忘记匹配解锁或提前 return,极易引发后续加锁失败。
- 必须成对使用
lock()/unlock(),或更推荐用std::lock_guard<:recursive_mutex></:recursive_mutex>/std::unique_lock<:recursive_mutex></:recursive_mutex>管理作用域 - 不能跨线程转移所有权:
std::unique_lock可移动,但移动后原对象变为未持有状态;std::lock_guard不可移动,也不可复制 - 若函数 A 调用 B,两者都尝试加同一把
std::recursive_mutex,B 中抛异常未 catch,而 A 的 guard 已析构,则无问题;但如果手动lock()后没配对unlock(),就会卡住 - 示例错误写法:
void bad_func() { mtx.lock(); // 第一次 if (cond) return; // 忘了解锁! mtx.lock(); // 第二次 // ... mtx.unlock(); mtx.unlock(); }
性能差异真的值得担心吗?
在绝大多数业务逻辑中,std::recursive_mutex 的额外开销(多一次线程 ID 比较、计数器增减)可以忽略。只有在极高频(纳秒级)临界区且确定无递归调用时,才值得为省几个周期换 std::mutex。
- 典型 x86-64 下,
std::recursive_mutex::lock()比std::mutex::lock()慢约 5–15 纳秒(取决于是否已持有) - 真正影响性能的是临界区长度,而不是锁类型本身;过长的临界区下,换锁类型毫无意义
- 如果怀疑瓶颈在此,先用 profiler(如 Linux 的
perf或 VTune)确认,不要凭直觉替换
替代方案:为什么有时该避免可重入?
可重入看似方便,实则掩盖设计问题。真实项目中,频繁需要递归加锁,往往说明职责耦合过紧或资源访问路径不清晰。
立即学习“C++免费学习笔记(深入)”;
- 重构建议:把共享状态封装成类,用 RAII 控制访问,而非裸锁 + 多层函数调用
- 若必须支持嵌套调用,考虑用「锁升级」模式(如读写锁 + 写锁降级),或引入锁顺序约定(如按地址排序加锁)来避免死锁,而非依赖可重入
- 注意:
std::recursive_mutex不解决跨线程死锁,只解决单线程重入;两个线程互相等待对方持有的递归锁,照样死锁


















