news 2026/4/29 10:33:55

两版本锁抛出异常测试

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
两版本锁抛出异常测试

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

文章目录

      • 核心思路
      • 完整对比示例代码
      • 关键运行结果与解释
        • 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版本。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/29 10:31:34

全网最全专科生必看TOP10 AI论文平台测评

全网最全专科生必看TOP10 AI论文平台测评 2026年专科生必备AI论文平台测评指南 在当前高等教育不断深化的背景下&#xff0c;专科生群体在学术写作方面面临着诸多挑战。从选题构思到文献检索&#xff0c;再到内容撰写与格式调整&#xff0c;每一个环节都可能成为阻碍论文完成的…

作者头像 李华
网站建设 2026/4/29 10:31:54

机械臂深度强化学习在MATLAB中的训练不收敛问题:系统化诊断与解决方案

机械臂深度强化学习在MATLAB中的训练不收敛问题:系统化诊断与解决方案 摘要 机械臂深度强化学习(Deep Reinforcement Learning, DRL)训练不收敛是常见且复杂的技术挑战。本文系统分析机械臂DRL训练不收敛的根本原因,提供全面诊断框架和解决方案,结合MATLAB具体实现,涵盖…

作者头像 李华
网站建设 2026/4/25 14:19:48

小程序毕设项目推荐-基于微信小程序的特色农产品团购平台设计与实现基于手机端的陕西地区特色农产品团购平台设计与实现小程序【附源码+文档,调试定制服务】

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/4/18 11:39:50

LFM信号与P4码组合调制波形的脉冲压缩与模糊函数分析

一、脉冲压缩实现 1.1 匹配滤波流程 频域匹配滤波&#xff1a;对接收信号进行FFT&#xff0c;与发射信号共轭频谱相乘。 时域卷积&#xff1a;采用FIR滤波器组实现脉冲压缩&#xff0c;压缩比 CRB⋅Tp。 多普勒补偿&#xff1a;通过相位补偿消除目标运动引起的频率偏移。 …

作者头像 李华
网站建设 2026/4/23 14:22:26

全网最全8个AI论文工具,助本科生轻松搞定毕业论文!

全网最全8个AI论文工具&#xff0c;助本科生轻松搞定毕业论文&#xff01; AI 工具让论文写作不再难 对于大多数本科生来说&#xff0c;撰写毕业论文是一项既重要又充满挑战的任务。面对繁重的写作压力、复杂的格式要求以及对学术规范的不熟悉&#xff0c;很多学生常常感到无从…

作者头像 李华
网站建设 2026/4/25 20:16:48

关于CPU的介绍(四)----CMO(Cache Maintenance Operations)

在阅读ACE协议spec的时候&#xff0c;有关于CMO的介绍&#xff0c;下面详细介绍下CMO操作&#xff1a; 一、CMO 的定义 CMO&#xff08;Cache Maintenance Operations&#xff0c;缓存维护操作&#xff09;是一套硬件指令或总线事务&#xff0c;用于主动管理缓存的状态&#…

作者头像 李华