std::recursive_mutex用于解决同一线程重入加锁问题,通过计数器允许重复lock/unlock,仅计数归零才释放锁;但性能开销大、易掩盖设计缺陷、调试困难且不适用于跨线程场景。

std::recursive_mutex 用来解决同一线程的重入加锁问题
当一个函数在持有锁的情况下又调用自身(递归),或调用另一个也尝试获取同一把锁的函数时,普通 std::mutex 会直接导致死锁——因为线程已持锁,再次 lock() 就会永远阻塞。而 std::recursive_mutex 允许同一线程反复 lock(),内部通过计数器跟踪加锁次数,只有对应次数的 unlock() 才真正释放锁。
和 std::mutex 的关键行为差异
二者接口几乎一致,但语义完全不同:
-
std::mutex:非递归,同一线程重复lock()→ 未定义行为(通常是死锁) -
std::recursive_mutex:递归安全,lock()增计数,unlock()减计数,仅当计数归零才释放所有权 - 性能开销略高:需维护线程 ID 和计数,且部分平台实现更重(如 Windows 上可能基于
CRITICAL_SECTION) - 不能与
std::unique_lock/std::shared_lock混用「自动析构释放」逻辑来简化嵌套?可以,但要注意:每次lock()都必须配对unlock(),否则计数不归零,其他线程永远拿不到锁
典型误用场景:以为“能递归”就该无脑用
实际中多数同步需求并不需要递归。滥用 std::recursive_mutex 往往掩盖设计缺陷:
- 函数职责不清,导致无意中重复进入临界区(比如 A 调 B,B 又调回 A)
- 本可通过重构(如拆出无锁逻辑、用 RAII 封装)避免嵌套加锁,却依赖递归机制硬扛
- 调试困难:锁状态不可见,计数不匹配时表现为“锁似乎没释放”,但错误位置可能离真实
unlock()缺失点很远 - 跨线程迁移风险:一旦某段逻辑被挪到另一线程执行,
std::recursive_mutex不再提供保护(它只认“同一线程”,不认“同逻辑”)
一个最小可验证示例
#include#include #include std::recursive_mutex rmtx; int value = 0;
void recursive_inc(int n) { rmtx.lock(); // 第一次成功;后续调用也成功 if (n > 0) { ++value; recursive_inc(n - 1); // 同一线程再次 lock() } rmtx.unlock(); // 对应本次 lock() }
int main() { std::thread t(recursive_inc, 3); t.join(); std::cout << "value = " << value << "\n"; // 输出 4 }
换成 std::mutex,这段代码在大多数实现上会卡死在第二次 lock()。
立即学习“C++免费学习笔记(深入)”;
递归锁不是银弹——它解决的是特定重入场景,但会让锁的生命周期变得隐式且难追踪。真要用,务必确保每次 lock() 都有明确对应的 unlock(),并且优先考虑是否能用更清晰的同步结构替代。










