C++中的std::recursive_mutex有什么用?(允许同一线程多次加锁)
技术百科
冰火之心
发布时间:2026-01-22
浏览: 次 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() 都有明确对应的 unlock(),并且优先考虑是否能用更清晰的同步结构替代。
# ai
# 的是
# 就会
# 都有
# 这段
# windows
# 会让
# 无意中
# 要用
# win
# 递归
# c++
# stream
# 接口
# 重构
# 线程
# 死锁
# red
# 无锁
# 封装
# ios
# 加锁
# 有锁
相关栏目:
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
AI推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
SEO优化<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
技术百科<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
谷歌推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
百度推广<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
网络营销<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
案例网站<?muma echo $count; ?>
】
<?muma
$count = M('archives')->where(['typeid'=>$field['id']])->count();
?>
【
精选文章<?muma echo $count; ?>
】
相关推荐
- Windows10系统怎么查看显卡型号_Win10
- 短链接怎么自定义还原php_修改解码规则适配需求【
- Windows10如何更改系统字体大小_Win10
- 静态属性修改会影响所有实例吗_php作用域操作符下
- Windows10电脑怎么查看硬盘通电时间_Win
- Win11怎么设置麦克风权限_允许应用访问Win1
- Win10如何卸载预装Edge扩展_Win10卸载
- Win11怎么关闭通知中心_Windows11系统
- php怎么下载安装后无法解析php文件_服务器配置
- Windows10如何更改计算机工作组_Win10
- Python网络日志追踪_请求定位解析【教程】
- c# 在ASP.NET Core中管理和取消后台任
- C#怎么使用委托和事件 C# delegate与e
- 如何在 Go 中创建包含 map 的 slice(
- ACF 教程:如何正确更新嵌套在多层 Group
- c# await 一个已经完成的Task会发生什么
- MAC怎么设置程序窗口永远最前_MAC窗口置顶插件
- php中$this和::能混用吗_对象与静态作用域
- Win11怎么压缩文件 Win11自带压缩解压功能
- c++中如何对数组进行排序_c++数组排序算法汇总
- 如何在Golang中实现CI/CD流水线自动化测试
- php8.4如何实现队列任务_php8.4redi
- c++的位运算怎么用 与、或、异或、移位操作详解【
- 如何使用Golang实现跨域请求支持_Golang
- Win10 BitLocker加密教程 Win10
- Python字符串操作教程_切片拼接与格式化详解
- C++如何获取CPU核心数?(std::threa
- 如何使用Golang defer优化性能_减少不必
- Windows10系统怎么查看运行时间_Win10
- Windows10系统怎么查看防火墙状态_Win1
- Python大型项目拆分策略_模块化解析【教程】
- Win11怎么关闭粘滞键_彻底禁用Windows
- Windows10怎么备份注册表_Windows1
- Go语言中slice追加操作的底层共享机制详解
- PHP接收参数值为空怎么办_判断和处理空参数方法说
- Win11怎么设置组合键快捷方式_Windows1
- Win11怎么关闭SmartScreen_禁用Wi
- C++ STL算法库怎么用?C++常用算法函数(s
- Win11怎么设置应用分屏_Windows11贴靠
- 如何用正则与预处理高效拦截带干扰符的恶意域名
- Mac如何解压zip和rar文件?(推荐免费工具)
- PowerShell怎么创建复杂的XML结构
- win11 OneDrive怎么彻底关闭 Win1
- 电脑的“网络和共享中心”去哪了_Windows 1
- 如何在Golang中使用replace替换模块_指
- Win11怎么查看局域网电脑_Windows 11
- Python深度学习实战教程_神经网络模型构建与训
- Windows服务无法启动错误1067是什么_进程
- Drupal 中渲染节点时出现 HTML 标签嵌套
- Win11摄像头无法使用怎么办_Win11相机隐私

QQ客服