当前位置: 首页 > news >正文

两版本锁抛出异常测试

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

      • 核心思路
      • 完整对比示例代码
      • 关键运行结果与解释
        • 1. 手动加锁版本(测试1)的问题
        • 2. lock_guard 版本(测试2)的正常表现
      • 总结

你想测试手动调用std::mutexlock()/unlock()方法时,临界区抛出异常并捕获的场景,对比std::lock_guard看看手动加锁是否存在缺陷(比如g_count无法累加至预设值、甚至死锁),我会为你编写手动加锁 vs lock_guard的对比示例,并详细解释核心问题。

核心思路

手动加锁的关键缺陷:如果临界区抛出异常,unlock()语句若写在异常之后,会因异常中断执行而永远无法解锁,导致互斥锁被永久占用(死锁),其他线程阻塞,最终g_count既达不到预设值,程序还可能卡死;而lock_guard基于 RAII 机制,即使异常也会触发析构函数自动解锁,不会出现这类问题。


完整对比示例代码

#include<iostream>#include<thread>#include<mutex>#include<vector>#include<stdexcept>#include<chrono>intg_count=0;std::mutex g_mutex;// ========== 手动加锁解锁(有缺陷版本) ==========voidincrementManual(inttimes){try{for(inti=0;i<times;++i){// 手动加锁g_mutex.lock();// 模拟临界区异常(第500次循环触发)if(i==500){std::cout<<"手动加锁线程:触发异常,unlock() 将无法执行!\n";throwstd::runtime_error("手动加锁:临界区异常");}g_count++;// 手动解锁(⚠️ 异常会跳过这行,锁永远无法释放)g_mutex.unlock();}}catch(conststd::exception&e){std::cout<<"手动加锁线程捕获异常:"<<e.what()<<"\n";// 【修复提示】手动加锁必须在这里手动解锁,否则死锁,但新手极易遗漏// g_mutex.unlock(); // 若注释掉这行,必然死锁;解开则能解锁,但代码易出错}}// ========== lock_guard 加锁(安全版本) ==========voidincrementGuard(inttimes){try{for(inti=0;i<times;++i){// RAII 加锁:构造时lock,析构时自动unlock(无论是否异常)std::lock_guard<std::mutex>lock(g_mutex);// 同样模拟临界区异常if(i==500){std::cout<<"lock_guard线程:触发异常,析构会自动解锁!\n";throwstd::runtime_error("lock_guard:临界区异常");}g_count++;}}catch(conststd::exception&e){// 仅捕获异常,无需手动解锁(lock_guard已自动处理)std::lock_guard<std::mutex>lock(g_mutex);// 加锁保证输出不乱码std::cout<<"lock_guard线程捕获异常:"<<e.what()<<"\n";}}intmain(){constintthread_num=5;constinttimes_per_thread=1000;std::vector<std::thread>threads;// ========== 测试1:手动加锁(会死锁/计数异常) ==========std::cout<<"===== 测试手动加锁(未手动解锁异常)=====\n";g_count=0;// 重置计数for(inti=0;i<thread_num;++i){threads.emplace_back(incrementManual,times_per_thread);}// 等待线程执行(⚠️ 这里会卡死,因为锁被永久占用)for(auto&t:threads){if(t.joinable()){t.join();// 程序会卡在这一行,无法继续}}std::cout<<"手动加锁最终count值:"<<g_count<<"\n";// 这行永远执行不到// ========== 测试2:lock_guard 加锁(正常执行) ==========// 【注意】先注释掉测试1的代码,再运行测试2,否则测试1卡死无法执行到这里// std::cout << "\n===== 测试lock_guard加锁(安全)=====\n";// g_count = 0;// threads.clear();// for (int i = 0; i < thread_num; ++i) {// threads.emplace_back(incrementGuard, times_per_thread);// }// for (auto& t : threads) {// if (t.joinable()) {// t.join();// }// }// std::cout << "lock_guard最终count值:" << g_count << "\n"; // 输出 2500return0;}

关键运行结果与解释

1. 手动加锁版本(测试1)的问题
  • 现象:程序运行后会输出「手动加锁线程:触发异常,unlock() 将无法执行!」,然后卡死在t.join()g_count的最终值永远打印不出来;
  • 核心原因
    • 线程执行到i=500时抛出异常,跳过了后续的g_mutex.unlock()
    • 互斥锁被该线程永久持有,其他线程调用g_mutex.lock()时会阻塞(拿不到锁),最终所有线程卡死,g_count通常远低于 2500(甚至只有几百);
    • 即使在catch块中手动加g_mutex.unlock()(解开代码中注释),也能解锁,但新手极易遗漏这行代码,且代码逻辑会变得繁琐(每个异常分支都要考虑解锁)。
2. lock_guard 版本(测试2)的正常表现
  • 现象:程序输出「lock_guard线程:触发异常,析构会自动解锁!」,无卡死,最终g_count输出2500(5个线程×每个执行500次);
  • 核心原因
    • std::lock_guard是 RAII 封装:构造时调用lock()无论正常执行还是异常退出作用域,析构函数都会自动调用unlock()
    • 即使抛出异常,锁也能被释放,其他线程可正常竞争锁,g_count能累加至预期值(每个线程执行到500次抛异常,总计 5×500=2500)。

总结

  1. 手动加锁的核心缺陷:异常会跳过unlock()语句,导致锁永久占用(死锁),g_count累加异常;即使手动在catch中解锁,代码也易出错、可读性差;
  2. lock_guard 的核心优势:RAII 机制保证「加锁-解锁」成对出现,无论临界区正常执行还是抛出异常,都能自动释放锁,避免死锁,计数结果符合预期;
  3. 实战建议:除非有特殊需求(如手动控制解锁时机),否则优先使用std::lock_guard(或std::unique_lock),避免手动调用lock()/unlock()

如果想测试「手动加锁但在catch中解锁」的修复版本,只需解开incrementManualcatch块里的g_mutex.unlock();注释,此时程序不会死锁,g_count也能输出 2500,但代码复杂度远高于lock_guard版本。

http://www.jsqmd.com/news/314452/

相关文章:

  • 【毕业设计】基于springboot的智能校园点餐管理系统设计小程序(源码+文档+远程调试,全bao定制等)
  • 初始C++: C++入门以及 类和对象初识
  • Web缓存投毒:从理论到大规模利用
  • Web缓存欺骗攻击:原理、实战与纵深防御
  • HTTP/2与HTTP/3协议的新型攻击面
  • HTTP Desync攻击的自动化检测与利用
  • J1939协议学习
  • 【小程序】微信小程序input设置readonly只读属性无效,这是怎么回事呢
  • 解冻
  • 高驰二手运动手表pacePro回收报价,支持全国上门回收
  • 优思学院|假设检验到底在检验什么?
  • 计算机毕业设计springboot博物馆藏品管理系统 基于Java的博物馆文物数字化保管平台 智慧博物馆馆藏资源信息管理系统
  • 计算机毕业设计springboot农产品溯源系统 基于SpringBoot的农产品电商与溯源一体化平台
  • 百泰派克生物科技:分子量分析,圆二色谱分析客户案例
  • Kiro Agent 最佳实践:构建生产级智能运维助手
  • 虚拟发动机控制器及物理模型设计说明书
  • 机械臂深度强化学习在MATLAB中的训练不收敛问题:系统化诊断与解决方案
  • STM32WB55蓝牙广播数据
  • [特殊字符]️_开发效率与运行性能的平衡艺术[20260128151042]
  • [特殊字符]_容器化部署的性能优化实战[20260128151543]
  • 企业微信外部群精准运营:API 主动推送消息开发指南
  • 【计算机毕业设计案例】基于微信小程序的驾校预约系统基于微信小程序的驾校考试预约系统的设计与实现(程序+文档+讲解+定制)
  • 【计算机毕业设计案例】基于手机端的地区特色农产品团购商城平台陕西地区特色农产品团购平台设计与实现小程序(程序+文档+讲解+定制)
  • 全网最全8个AI论文工具,助本科生轻松搞定毕业论文!
  • 计算机毕业设计springboot小说阅读平台的设计与实现 基于SpringBoot的在线文学阅览系统的设计与实现 SpringBoot框架驱动的数字化网络小说管理平台构建
  • 【鸿蒙 PC 命令行适配】基于 lycium 适配鸿蒙版 Nginx 的解决方案
  • 拆解 Moltbot:一个”本地优先”的 AI 助手,到底是怎么跑起来的?
  • 回不去的十年:当算法填满了所有缝隙,我们究竟失去了什么?
  • 企业微信 API 开发干货:外部群消息主动推送核心逻辑
  • 2026年智慧能源整体解决方案 - 全1026页下载