多线程之互斥锁与死锁:一个真实死锁bug让我排查了整整两天
上篇把线程创建与管理聊了。今天聊多线程里最让人头疼的问题——数据竞争和死锁。先讲个真实的bug故事。之前做一个多传感器融合的项目激光雷达和IMU的数据分别由两个线程读取融合之后发布。上线测试的时候程序跑着跑着就卡死了——不是崩溃是卡住不动了。没有任何错误日志就是所有线程都不干活了。排查了两天最后发现是死锁。问题出在哪两个线程各自持有两把锁但加锁顺序不一样// 线程A先锁lidar_mutex再锁fusion_mutex void lidarThread() { while (running_) { lock_guardmutex lock1(lidar_mutex_); auto scan latestScan_; lock_guardmutex lock2(fusion_mutex_); // 等fusion_mutex fusionData_.lidar scan; } } // 线程B先锁fusion_mutex再锁imu_mutex void imuThread() { while (running_) { lock_guardmutex lock1(imu_mutex_); auto imu latestImu_; lock_guardmutex lock2(fusion_mutex_); // 也等fusion_mutex fusionData_.imu imu; } }某个时刻线程A拿了lidar_mutex_等fusion_mutex_线程B拿了imu_mutex_也要等fusion_mutex_——等等这其实不是死锁只是竞争。真正的死锁是互相等对方手里的锁。让我还原一下当时的真实场景// 真正的死锁场景 void threadA() { mutex1_.lock(); // A拿了mutex1 // ... 做一些事 mutex2_.lock(); // A等mutex2 } void threadB() { mutex2_.lock(); // B拿了mutex2 // ... 做一些事 mutex1_.lock(); // B等mutex1 // 死锁A等B的mutex2B等A的mutex1 }mutex的基本用法C11提供了std::mutex来解决数据竞争。基本用法很简单mutex mtx; int sharedCounter 0; void increment() { mtx.lock(); sharedCounter; // 临界区同一时间只有一个线程能访问 mtx.unlock(); }但直接用lock/unlock很危险——如果临界区里抛了异常unlock不会被执行锁就永远不会释放。所以C推荐使用RAII方式的lock_guard或unique_lockvoid increment() { lock_guardmutex lock(mtx); // 构造时加锁 sharedCounter; // 析构时自动解锁即使抛异常也安全 }lock_guard是最简单的锁包装——构造加锁析构解锁没有别的选项。unique_lock更灵活支持延迟加锁、手动解锁、try_lockvoid flexibleLock() { unique_lockmutex lock(mtx, defer_lock); // 先不锁 // 做一些不需要锁的事 prepareData(); lock.lock(); // 现在才加锁 criticalSection(); lock.unlock(); // 可以手动解锁 // 非阻塞尝试 if (lock.try_lock()) { // 拿到锁了 } else { // 没拿到做别的事 } }死锁的四个必要条件死锁的发生需要同时满足四个条件互斥——资源同一时间只能被一个线程持有。锁天然是互斥的这个没法消除。持有并等待——线程持有一把锁的同时等待另一把锁。不可抢占——锁不能被强制从持有者手里夺走。循环等待——线程A等B持有的锁B等C持有的锁C又等A持有的锁。要防止死锁只要打破其中任意一个条件就行。最实用的做法是打破循环等待——让所有线程按相同的顺序加锁。// 解决方案统一加锁顺序 void threadA() { lock_guardmutex lock1(mutex1_); // 先锁1 lock_guardmutex lock2(mutex2_); // 再锁2 } void threadB() { lock_guardmutex lock1(mutex1_); // 同样的顺序 lock_guardmutex lock2(mutex2_); // 先锁1再锁2 }C17还提供了一个更方便的工具——std::lock可以同时锁多把锁而不死锁void transferMoney(Account from, Account to, double amount) { // std::lock同时锁两个mutex内部用try-and-back-off避免死锁 lock(from.mutex_, to.mutex_); lock_guardmutex lockFrom(from.mutex_, adopt_lock); lock_guardmutex lockTo(to.mutex_, adopt_lock); from.balance_ - amount; to.balance_ amount; }机器人开发中的典型数据竞争在机器人系统里数据竞争最常出现在传感器数据和共享状态上。class SensorFusion { mutex mtx_; Pose currentPose_; public: void updateFromImu(const ImuData data) { lock_guardmutex lock(mtx_); currentPose_ integrateImu(data); } void updateFromLidar(const LidarScan scan) { lock_guardmutex lock(mtx_); currentPose_ matchLidar(scan); } Pose getPose() { lock_guardmutex lock(mtx_); return currentPose_; } };这个设计是对的但要注意锁的粒度。如果临界区太大锁住了大量计算其他线程就得等着并行度下降。如果临界区太小只锁了一个赋值可能保护不够。经验法则是只锁住访问共享数据的那几行代码计算逻辑放在锁外面。面试中的关键考点mutex和spinlock有什么区别mutex拿不到锁时会阻塞线程让出CPUspinlock拿不到锁时会一直循环尝试忙等不让出CPU。mutex适合临界区较长的场景spinlock适合临界区极短几个指令的场景。recursive_mutex是什么允许同一个线程对同一把mutex多次加锁而不会死锁。但一般不推荐用——如果你发现需要recursive_mutex多半是设计有问题。怎么排查死锁Linux下可以用gdb attach到进程看每个线程的调用栈找出各自持有什么锁、在等什么锁。工具层面可以用ThreadSanitizerTSan编译时加-fsanitizethread就能检测潜在的数据竞争。给正在准备面试的你互斥锁和死锁是面试高频考点。基本的mutex用法必须熟练lock_guard和unique_lock的区别要能讲清楚。死锁的四个条件和预防方法也要能说出来。最重要的是写代码的时候要有锁的意识。拿到一把锁的时候脑子里就要想清楚——这把锁和其他锁有没有顺序冲突会不会和其他线程的加锁顺序形成环路养成这个好习惯能帮你避免绝大多数死锁问题在面试和实际项目开发中都非常有用。下篇聊条件变量——生产者消费者模型在传感器数据流中的实际应用。这是多线程协作的核心机制和重要基础。如果这篇文章对你有帮助欢迎点赞、在看、转发三连。 你的支持是我持续更新的最大动力。「机器人软件开发面试·从入门到精通」连载系列上一篇第54篇 多线程之线程创建与管理——机器人系统中为什么必须用多线程 下一篇预告第56篇 多线程之条件变量——生产者消费者模型在传感器数据中的应用有任何问题欢迎评论区留言我会尽量回复。

相关新闻

Tiva TM4C123BE6PM管脚功能详解与配置实战

Tiva TM4C123BE6PM管脚功能详解与配置实战

1. 项目概述与核心价值对于任何一位嵌入式硬件工程师或单片机开发者而言,拿到一颗新的微控制器(MCU)后,第一件要紧事就是“啃”数据手册里的管脚定义表。这看似枯燥的表格,却是连接芯片内部强大功能与外部现实世界的唯…

2026/8/24 5:41:15 阅读更多 →
多线程之线程创建与管理:面试官问“机器人系统为什么必须用多线程”

多线程之线程创建与管理:面试官问“机器人系统为什么必须用多线程”

从今天开始进入多线程板块。这块内容在C面试里分量很重——互斥锁、死锁、条件变量、原子操作、线程池,一连串的话题,每个都能聊一篇。先从最基础的开始:线程的创建与管理。为什么要用多线程?这个问题在机器人开发里答案特别明确&…

2026/8/23 5:43:21 阅读更多 →
2024年7月AI大模型技术报告:Gemini、Grok、GPT最新突破与编程模型格局

2024年7月AI大模型技术报告:Gemini、Grok、GPT最新突破与编程模型格局

最近在AI领域真是热闹非凡,各大厂商都在密集发布新模型和功能更新。如果你正在关注AI大模型的最新进展,特别是Gemini、Grok和GPT系列的最新动态,那么本文正好为你整理了一份完整的7月AI技术发展报告。本文将详细解析Gemini 3.5 Pro的发布预测…

2026/8/22 18:22:06 阅读更多 →

最新新闻

AI编程助手会话持久化:从数据序列化到状态恢复的工程实践

AI编程助手会话持久化:从数据序列化到状态恢复的工程实践

1. 会话持久化的核心价值与挑战在AI编程助手的使用中,最令人沮丧的体验莫过于:你花了一个小时与Claude Code讨论一个复杂的重构方案,中途因为网络波动、浏览器崩溃或者需要换个设备继续工作,导致整个对话历史丢失。一切又得从头开…

2026/8/24 5:42:55 阅读更多 →
Python招聘数据分析系统:架构设计与反爬策略

Python招聘数据分析系统:架构设计与反爬策略

1. 项目背景与核心价值最近在帮朋友做职业规划咨询时,发现市场上缺乏实时、结构化的行业人才需求分析报告。传统招聘网站虽然数据丰富,但缺乏深度挖掘工具。于是我用Python开发了一套招聘数据采集分析系统,专门针对国内主流招聘平台进行行业趋…

2026/8/24 5:42:55 阅读更多 →
苏州智造转型:工业互联网与数字孪生技术实战解析

苏州智造转型:工业互联网与数字孪生技术实战解析

苏州,这座被称为“最强地级市”的城市,正在经历一场深刻的产业变革。当“工业4.0”、“智能制造”这些概念在各地被反复提及,甚至有些审美疲劳时,苏州的“智造”之路却呈现出一种截然不同的务实与凶猛。它没有停留在口号和规划上&…

2026/8/24 5:42:55 阅读更多 →
CentOS与Ubuntu服务器等保2.0合规实战:从身份鉴别到安全审计的完整加固指南

CentOS与Ubuntu服务器等保2.0合规实战:从身份鉴别到安全审计的完整加固指南

1. 项目概述:从合规压力到实战落地最近在帮几个客户做等保测评的整改,清一色都是CentOS和Ubuntu服务器。每次测评报告下来,看着那一长串的“中危”、“高危”漏洞和不符合项,甲方运维兄弟的脸色都不太好看。这活儿干多了&#xff…

2026/8/24 5:42:55 阅读更多 →
基于SSM框架的火车票预售系统:高并发锁机制与事务管理实战

基于SSM框架的火车票预售系统:高并发锁机制与事务管理实战

如果你是一名Java开发者,或者正在为毕业设计寻找一个既有商业价值又能体现技术综合性的项目,那么“火车票预售系统”很可能已经出现在你的备选清单里。这个选题看似传统,但每年都能成为热门,背后原因很简单:它几乎涵盖…

2026/8/24 5:42:55 阅读更多 →
无显示器获取树莓派IP的4种实战方法:从路由器到nmap扫描

无显示器获取树莓派IP的4种实战方法:从路由器到nmap扫描

1. 从“盲盒”到“握手”:无显示器玩转树莓派的起点 刚拿到树莓派4B,兴冲冲地准备大干一场,结果发现手边没有多余的显示器,甚至连一根HDMI线都找不到。这大概是很多朋友入门树莓派时遇到的第一个“下马威”。你看着这块小巧的板子…

2026/8/24 5:41:55 阅读更多 →

日新闻

前端内容安全与依赖审计实践

前端内容安全与依赖审计实践

前端内容安全与依赖审计实践 前端安全依赖分层防护。没有任何单一配置能替代输出编码、权限校验和依赖更新。 把不可信内容当作数据 默认使用框架的转义能力;确需渲染 HTML 时,先在服务端或可信的客户端库中进行白名单过滤。避免把用户输入直接赋给 inne…

2026/8/24 1:08:15 阅读更多 →
Windows登录密码存储机制全解析:从哈希算法到安全加固实战

Windows登录密码存储机制全解析:从哈希算法到安全加固实战

1. 项目概述:Windows登录密码的“黑匣子”每次你按下CtrlAltDel,输入密码,然后看到那个熟悉的桌面,这背后发生了一系列复杂而精密的操作。作为一名长期与Windows系统打交道的从业者,我经常被问到:“我的密码…

2026/8/24 1:08:15 阅读更多 →
AI面试系统安全挑战与解决方案

AI面试系统安全挑战与解决方案

1. 项目概述:AI面试系统的安全挑战去年参与某跨国企业AI面试系统部署时,遇到一个典型案例:候选人在视频面试中无意提到竞争对手产品名称,系统竟自动将该信息关联到企业知识库并生成竞品分析报告。这个看似"智能"的功能&…

2026/8/24 1:08:15 阅读更多 →

周新闻

[光学原理与应用-521]:对光的错误理解与纠偏

[光学原理与应用-521]:对光的错误理解与纠偏

首先光是一种能量的载体和形态,宏观上观察到的光是由无数个微观的光量子组成的,每个光子在产生的瞬间,其在真空的空间中以确定不变的速度沿着一个初始的方向一直向前,在微观层面,每个光量子的运动轨迹是以波函数所展现…

2026/8/24 0:06:02 阅读更多 →
SIP通话转接原理与REFER方法实战解析

SIP通话转接原理与REFER方法实战解析

1. 通话转接不是“挂断再拨号”,而是SIP会话的动态重定向你有没有遇到过这样的场景:客服坐席A正在和客户通电话,突然需要把这通对话无缝转给专家坐席B,客户完全感知不到中间的断连——既没听到忙音,也没被要求重新拨号…

2026/8/24 0:20:20 阅读更多 →
Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

Kolla-ansible单节点OpenStack部署实战:从环境准备到排坑指南

1. 为什么选择Kolla-ansible来部署单节点OpenStack?如果你正在寻找一种能把OpenStack从“概念”快速变成“可用的实验环境”的方法,那么Kolla-ansible几乎是当前最主流、最省心的选择。我见过太多人卡在手动编译依赖、配置服务、处理版本冲突的泥潭里&am…

2026/8/24 0:14:11 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/23 18:47:06 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/23 12:10:44 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/22 3:22:48 阅读更多 →