news 2026/5/1 6:58:03

两版本锁抛出异常测试

作者头像

张小明

前端开发工程师

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/5/1 6:26:46

计算机毕业设计springboot鄂豫皖苏区首府革命博物馆 基于SpringBoot的红色文化遗产数字化保护与服务平台 SpringBoot框架下的革命纪念馆综合业务管理系统

计算机毕业设计springboot鄂豫皖苏区首府革命博物馆1fq6wq56 &#xff08;配套有源码 程序 mysql数据库 论文&#xff09; 本套源码可以在文本联xi,先看具体系统功能演示视频领取&#xff0c;可分享源码参考。 鄂豫皖苏区作为中国革命历史上的重要根据地&#xff0c;承载着厚重…

作者头像 李华
网站建设 2026/5/1 6:26:27

【课程设计/毕业设计】基于手机端的某地区特色农产品团购平台基于手机端的陕西地区特色农产品团购平台设计与实现小程序【附源码、数据库、万字文档】

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

作者头像 李华
网站建设 2026/4/22 17:29:05

回不去的十年:当算法填满了所有缝隙,我们究竟失去了什么?

最近在做具身智能和教育引擎的时候&#xff0c;我常在想一个问题&#xff1a;现在的孩子&#xff0c;未来还会拥有关于“等待”和“粗糙”的记忆吗&#xff1f; 过去十年&#xff0c;是中国社会被代码和算法彻底重构的十年。我们确实跑得更快了&#xff0c;但在这种极速中&…

作者头像 李华
网站建设 2026/4/25 11:10:59

小程序毕设项目:基于手机端的陕西地区特色农产品团购平台设计与实现小程序(源码+文档,讲解、调试运行,定制等)

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

作者头像 李华
网站建设 2026/5/1 6:25:17

HN2300_N沟道20V5.2A MOSFET场效应管应用场景分析

HN2300 是 SOT-23 封装 N 沟道增强型 MOSFET&#xff0c;主打低压大电流开关应用&#xff0c;性价比高、供应链稳定&#xff0c;广泛用于消费电子与小家电。HN2300典型应用电路&#xff08;核心场景&#xff09; 1. 锂电池保护 / 负载开关&#xff08;最常用&#xff09; 核心功…

作者头像 李华
网站建设 2026/5/1 6:25:22

小程序毕设选题推荐:基于springboot的文物知识科普“江西文物时讯”微信小程序【附源码、mysql、文档、调试+代码讲解+全bao等】

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

作者头像 李华