傍晚下班高峰,城市中心一个没有红绿灯的十字路口,四辆车同时抵达。北面来的车想直行到南边,却被东面来的车挡住了去路;东面的车要左转,却被南面的车挡住;南面的车想右转,又被西面的车挡住;西面的车刚要直行,北面的车又堵在正前方。四辆车谁也不肯后退,喇叭声此起彼伏,整个路口陷入奇妙的僵局——这就是死锁的活写照。 在计算机世界里,进程之间争抢 CPU、内存、打印机这类“资源”时,也会发生一模一样的僵局:每个进程都握着部分资源不放,同时等待其他进程释放资源,结果大家都卡在原地,系统像被按下了暂停键。这篇文章带你认识死锁的定义与四个必要条件,学会用“资源分配图”这个侦察工具揪出潜在的环路,再逐一了解破坏四个条件的预防策略,最后算一笔“安全与效率”的经济账——就像为路口加装红绿灯,必然会牺牲一点“随心所欲”的通行效率。
对 AI 从业者而言,死锁与预防是并发系统稳定性的地基:分布式训练中的显存竞争、推理服务的多线程锁、特征工程里的数据库连接池,全都绕不开它,也是系统设计与面试的高频考点。
可视化演示
通过Coffman四必要条件、资源分配图循环检测和预防策略对比三个模式,直观展示死锁成因与消除机制。
本节配有交互动效(进入视口后自动加载,约 3KB)。
Coffman死锁四必要条件 互斥:资源同一时刻只能被一个进程占用 循环等待:进程间形成资源请求环 互斥 ∧ 占有等待 ∧ 不剥夺 ∧ 循环等待 四条件同时成立才会发生死锁
死锁基本概念
傍晚的十字路口,四辆车各占一角。北车要直行,被东车挡住;东车要左转,被南车挡住;南车要右转,被西车挡住;西车要直行,又被北车挡住。四辆车都握着自己的位置不放,又都等着别人先动,喇叭声吵成一片,却没有一辆车能挪动半步。这个场景,就是操作系统教材里“死锁”一词最形象的注脚。
把车换成进程,把车道换成资源——内存、打印机、显存、文件锁——一切立刻对得上号。进程 A 持有打印机,想用扫描仪,而进程 B 持有扫描仪,正等着打印机释放。两个进程都“占着位置不干活”,互相观望,谁也无法继续执行。死锁不是程序报错,不是崩溃,而是一种安静而顽固的停滞。
严谨地说,死锁是指一组进程中的每一个进程,都在等待一个“仅由该组内其他进程才能产生”的事件(通常是释放资源)。参与死锁的进程全部处于阻塞状态,资源被白白占用,系统吞吐量降为零。它不消耗 CPU,却让整个系统形同瘫痪,因此是并发系统设计中最隐蔽的敌人之一。
正如四辆车里的任何一辆都无法指望路口外的行人来指挥交通,死锁中的每个进程也只能干等组内“队友”——而队友同样在等它。理解了这种互相成全的停滞,我们才能继续追问:什么样的条件凑在一起,才会酿造出这样的僵局?
- 进程集合 S死锁考察的不是单个进程,而是一组互相牵扯的进程,就像十字路口上四辆车必须整体分析。
- 等待事件每个进程都在等一个外部事件,最常见的就是“别人释放资源”这个事件。
- 组内互等能解除等待的只有同组的其他进程,外部进程无法介入,这是僵局的核心。
- 双向蕴含互推关系表示两边互为充要条件,定义既精确又完整,可以用来严格判定死锁。
四个必要条件
仔细盯着那个十字路口看,你会发现四辆车能僵住,其实缺一不可。首先,路口同一时刻只能过一辆车(互斥);其次,每辆车都占着当前车道、还想要前面的路段(占有并等待);再次,没有车愿意主动倒车,交警也没来(不可剥夺);最后,四辆车刚好围成一个“你等我、我等它”的圈(循环等待)。
计算机科学家把这几件事总结为死锁的四个必要条件:互斥条件、占有并等待、不可剥夺、循环等待。四者必须同时成立,死锁才会发生;反过来,只要破坏其中任何一个,死锁就不可能形成——这正是后面所有预防策略的理论总纲。
用逻辑语言看,四个条件合起来是死锁的充分条件,而每个条件单独看都是死锁的必要条件。这意味着:系统没有死锁,不代表四个条件都不满足;但系统一旦死锁,四个条件必然全部在场。
四辆车要想脱困,要么拓宽路口、要么提前约定通行顺序、要么请交警强制指挥——对应到系统里,就是下一节起我们要逐一拆解的四大预防策略。记住这张“四条件清单”,后面的故事全是围绕它展开的。
\displaystyle \text{有死锁} \Rightarrow M \wedge B \wedge N \wedge C,\quadM=\text{互斥},\ B=\text{占有并等待},\ N=\text{不可剥夺},\ C=\text{循环等待}- 互斥条件 M同一时刻资源只能被一个进程使用,像路口同一时间只能过一辆车。
- 占有并等待 B进程占着已有资源不放手,同时又申请新资源,像车占着车道还想要前方路段。
- 不可剥夺 N资源只能由持有者主动释放,系统不能强行收走,像谁都不肯主动倒车。
- 循环等待 C进程之间形成一环扣一环的等待链,像四辆车首尾相接围成一圈。
- 合取关系四个条件用“与”连接,任何一个条件缺席,死锁都不成立。
资源分配图
要在十字路口判断“会不会堵死”,最直观的办法是把每辆车和每条车道画成一张关系图:车要用的车道,画一条指向车道的线;车道正被某辆车占着,就画一条从车道指向车的线。这张图一摆出来,谁在等谁、谁会等成环,一目了然。
操作系统里这叫资源分配图(Resource Allocation Graph):圆圈表示进程,方框(内部加点表示实例数量)表示资源类型;进程指向资源的边叫请求边,资源指向进程的边叫分配边。图上的每一条边,都是一句“我想要你”或“它属于我”的声明。
判定规则非常漂亮:若图中无环,系统一定不会死锁;若图中有环,且每个资源都是单实例(路上只有一条车道),则系统必然死锁。多实例资源时,环只是死锁的必要条件——环里可能还有剩余实例“救场”。
回到十字路口:画好这张图,你一眼就能看到一条“A→车道→B→车道→C→车道→D→车道→A”的闭环。发现环之后怎么办?交警上场,开始拆条件——这正是下一节要讲的重点。
\displaystyle G = (P \cup R, E),\quad E = E_{req} \cup E_{alloc},\quadE_{req} = \{(p_i, r_j)\},\quad E_{alloc} = \{(r_j, p_i)\}- 两类节点图中只出现进程与资源两类元素,分别画成圆圈和方框。
- 两类边请求边表示“我想用”,分配边表示“我正用着”,方向不同,含义相反。
- 环路检测沿着有向边走能回到起点,就找到了一条环,也就是循环等待的图影。
- 判定结论单实例资源下环等价于死锁;多实例资源下,还要看环内资源是否有剩余实例。
破坏互斥条件
要让四辆车不再互相堵死,最“釜底抽薪”的思路是:把路口修成立交桥,所有车各走各的道,谁也不用等谁。对应到计算机里,就是破坏互斥条件——让资源可以被多个进程同时使用。
遗憾的是,互斥是许多资源与生俱来的属性:打印机不可能同时打印两份文档,磁带机不可能同时写两个任务,显存缓冲区也不方便被两个内核同时写入。强行取消互斥,要么违反物理现实,要么引入新的数据混乱。因此,“破坏互斥”在四种策略里是适用面最窄的一种。
真正的工程解法是“曲线救国”——假脱机(SPOOLing)技术。以打印机为例,系统先让各进程把输出写到磁盘上的打印队列,由专门的假脱机进程统一排队、独占打印。进程看来打印机变成了“共享设备”,互斥被悄悄绕开,死锁自然无从谈起。代价是额外的磁盘空间与延迟。
立交桥造价高昂、不是每个路口都修得起。破坏互斥条件也一样:只对少数天然可共享的资源有效,应用场景局限。既然这条路常常走不通,交警把目光转向第二招——从源头上禁止“边占边等”。
- 否定互斥让资源可以被多个进程同时访问,从根本上消除“一个萝卜一个坑”的争抢。
- 蕴含翻译死锁的必要条件被拆掉一根支柱,整座建筑随之倒塌。
- 现实约束只有天然可共享的资源才能直接改造;独占设备要靠假脱机缓冲间接实现共享。
破坏占有等待
四辆车之所以形成僵局,是因为每辆车都“先占了一条道,再算计前面的路”。如果换个规则:谁想上路,必须一次性把整条路线的通行权全部申请到手,否则就不准上路,那么要么四辆车畅通无阻依次放行,要么谁也没资格进场——僵局自然不存在。
这就是“破坏占有并等待”策略,也称资源预分配:进程运行前一次性申请它需要的全部资源,系统要么全部分配,要么一个都不分配。进程在执行期间不再提出新的资源请求,也就断掉了“占着等”的中间状态。
代价同样明显:资源利用率大幅下降——进程也许只用到打印机十分钟,却要独占它整个生命周期;更糟的是饥饿问题,低优先级进程可能永远等不齐它要的资源,出现“饿死”现象。因此这种策略只适合任务时间短、资源需求明确的场景(例如数据库事务的预锁)。
一次性分配就像要求四辆车出发前先摇到“整条路线的同时通行权”,安全归安全,却让本来就紧张的车道大量闲置。交警很快发现,还有更灵巧的第三招——允许强制倒车。
- 持有与需求分别记录进程已占资源与正在等待的资源,作为约束的对象。
- 析取约束任意时刻两者不能同时非空,“占有并等待”的中间态被彻底排除。
- 预分配保障所有资源在执行前一次性分配到位,执行期间不再产生新的申请。
- 代价投射长期独占资源导致利用率下降,低优先级进程可能永远等不齐资源。
破坏不可剥夺
现实生活里,死堵的路口往往靠交警一声令下:“那辆车,往后倒!”虽然司机不情愿,但倒车之后,僵局立刻松动。操作系统里同样可以引入这位“交警”——剥夺机制。
破坏不可剥夺条件的基本思想是:当一个进程申请不到新资源时,系统可以强行回收它已占有的部分资源,重新分配给等待中的进程。持有者被剥夺资源后,必须回滚到安全状态,等将来再重新申请、重新执行。
这个策略对“状态可保存、可恢复”的资源非常有效。CPU 的寄存器、内存页表都是好例子:中断一下,保存现场,资源就能转给别人。但打印机不行——打了一半的文档没法“回滚”重来。所以剥夺策略的应用范围,取决于资源的可回滚程度。
交警的强制倒车不是万能的——倒出去的是一辆车,可它刚走过的路径如果无法还原(文档打了半页),这条路就行不通。于是,最优雅也最常用的第四招登场了:从规则上规定谁先走、谁后走。
- 强制回收系统把进程已占资源全部清空,如同交警命令倒车腾出车道。
- 状态回滚进程无法从当前进度继续,只能回到被剥夺前的安全现场,重新开始。
- 可回滚前提只有状态可完整保存与恢复的资源,才经得起这种“倒车再出发”。
破坏循环等待
最后也是工程上最实用的思路:不碰车、不抢道,只改一条规则——给所有路口编上号,任何车只能往编号更大的路口开,禁止“回头”申请。这样无论怎么开,都不可能绕成一圈:因为路线的编号严格递增,根本没有回到起点的可能。
这就是破坏循环等待的资源编号法:系统为每种资源分配一个唯一的全局编号,规定所有进程必须按照编号递增的顺序申请资源。以后要想申请编号较小的资源,必须先释放手中编号较大的资源。环路需要的“反向申请”被规则彻底禁止,从结构上杜绝了死锁。
编号法实现简单、开销低,是实际操作系统的首选预防策略。但设计者必须谨慎设定编号:编号顺序会影响资源使用的灵活性,而且新增资源类型的编号划定、对已有代码的申请顺序改造,都需要在系统设计初期就定好规矩,后期很难更改。
让所有车都往“大号路口”开,听起来有点绕路,却用最轻的代价拆掉了那个致命闭环。四种预防策略讲完,最后摆在我们面前的问题是:这些“安全措施”本身,要花多少钱?
- 全局编号为每类资源分配一个互不相同的自然数编号,像给每个路口挂一块唯一门牌。
- 递增申请进程每一次新申请,目标资源的编号必须比已持有资源更大。
- 禁止反向环路中必有一条编号下降的“回头”边,而这条边被规则堵死。
- 工程启示编号一经发布便难以修改,系统设计初期就要完成全局资源编号规划。
权衡与工程决策
红绿灯能防止路口死锁,但代价是车流随到随走的自由;立交桥也是同样的道理。操作系统里的每一种死锁预防策略,本质都是用资源利用率和系统吞吐量去交换“绝对安全”。理解了这笔账,才谈得上工程选型。
一次性预分配让资源长期闲置,利用率跌得最惨;剥夺和回滚让系统忙于“存档读档”,额外开销巨大;编号法则在灵活性和安全性之间找到了较好的平衡点,因此最常被实战采用。破坏互斥的应用面最窄,通常只在假脱机这类特定场景出现。
衡量这笔交易,可以看两个指标:资源利用率 U(资源真正干活的时间占比)和系统吞吐量 Θ(单位时间完成的进程数)。预防策略越激进,U 和 Θ 往往越低;但死锁一旦发生,U 和 Θ 会直接归零。工程决策的实质,就是在“预防成本”与“死锁损失”之间找最小代价。
回到最初的十字路口:如果没有交警、没有红绿灯,四辆车只能靠“谁都不让”的默契堵死在那里;而有了合理规则,哪怕规则让人多等几秒、多绕几步,整条街反而能川流不息。操作系统的死锁预防,说到底也是这个道理——用一点点效率,换整个系统永不停摆的稳定。
- 资源利用率资源实际使用时间占总时间的比例,预分配会让分母变大、利用率下降。
- 系统吞吐量单位时间内完成的进程数量,回滚与等待都会拖累这个数字。
- 权衡关系利用率和吞吐量越高,资源竞争越激烈,死锁风险也随之上升。
- 实践倾向综合成本与可控性,资源编号递增申请是工业界最常用的预防手段。
AI 应用场景
分布式训练中的通信死锁预防
阶段:模型训练与分布式资源调度
解决的问题:多机多卡训练时,各进程按任意顺序申请通信缓冲区和梯度锁,容易像四辆车一样互相等待。借鉴“资源编号递增申请”的思路,框架为设备与缓冲区统一编号,所有 rank 按编号递增顺序提交申请,从结构上切断环形等待,避免集群训练整体卡死。
具体案例:某大模型团队在 8 卡数据并行训练时,偶尔出现 all-reduce 卡住 30 秒的告警;排查确认是两个 rank 同时持有一个通信缓冲、又都等待对方释放最终梯度。改造为按 rank 编号排序后建立通信链路,死锁彻底消失,训练吞吐提升约 12%。
AI 推理服务的多线程锁排序
阶段:推理服务的并发控制
解决的问题:批量推理引擎中多个线程同时访问共享模型权重与结果队列,若线程 1 持锁 A 等锁 B、线程 2 持锁 B 等锁 A,服务会整体停顿。采用破坏循环等待的锁编号法,规定所有线程按锁 ID 递增加锁,杜绝锁顺序反转。
具体案例:某 NLP 在线服务的 batch 推理引擎曾因两级锁顺序不一致导致偶发 3 秒长停顿,按锁 ID 排序改造后,线上死锁类告警归零,P99 时延下降 8%。
特征工程数据库连接池的预分配
阶段:数据采集与特征存储
解决的问题:特征回填任务多线程同时取数据库连接做两段式更新时,线程可能持有连接 A 等待连接 B,另一线程反之。采用“一次性申请全部连接再执行、并设置获取超时”的预分配策略,使“占有并等待”无法同时成立。
具体案例:广告推荐系统的特征回填曾因连接池死锁导致数据延迟 1 小时;改造为 try-acquire 全量连接 + 超时释放后,任务稳定性显著提升,未再出现集体卡死。
Python 演示
#!/usr/bin/env python3# -*- coding: utf-8 -*-"""死锁检测与预防演示:等待图判环 + 编号递增申请""" def build_wait_graph(procs): """procs: {pid: (held, need)}, 返回等待图 {pid: [等待哪些pid]}""" holder = {} for pid, (held, need) in procs.items(): for r in held: holder[r] = pid graph = {pid: [] for pid in procs} for pid, (held, need) in procs.items(): for r in need: if r in holder and holder[r] != pid: graph[pid].append(holder[r]) return graph def has_cycle(graph): """DFS 三色标记法检测环路""" WHITE, GRAY, BLACK = 0, 1, 2 color = {pid: WHITE for pid in graph} def dfs(pid): color[pid] = GRAY for nxt in graph[pid]: if color[nxt] == GRAY: return True if color[nxt] == WHITE and dfs(nxt): return True color[pid] = BLACK return False return any(color[pid] == WHITE and dfs(pid) for pid in graph) def show(name, procs): graph = build_wait_graph(procs) print(name) print(" 等待图:", graph) print(" 死锁?", "是" if has_cycle(graph) else "否") def main(): # 场景一: P1 占 R1 要 R2, P2 占 R2 要 R1, 典型死锁 show("场景一: 交叉等待", { "P1": (["R1"], ["R2"]), "P2": (["R2"], ["R1"]), }) # 场景二: 编号法预防 (R1 编号 1 < R2 编号 2) # P2 想申请 R1 必须先释放 R2, 于是先释放再申请, 不构成反向持有 show("场景二: 编号法预防后", { "P1": (["R1"], ["R2"]), "P2": ([], ["R2"]), }) if __name__ == "__main__": main()
holder[r] = pid登记每个资源当前被哪个进程占用,就像记下“每个路口被哪辆车占着”。graph[pid].append(holder[r])进程需要的资源被别人占着,就画一条“等待”边,完成等待图的构建。color[pid] = GRAYDFS 深入时把节点标记为灰色,表示“正在这条链上往下走”。if color[nxt] == GRAY: return True递归链上再次遇到灰色节点,说明绕回自己,找到环形等待。"P2": ([], ["R2"])编号法让 P2 先释放低编号资源,打断环路,死锁不再成立。
常见误区
误区资源分配图里只要出现一个环,系统就必然死锁了,所以画图找环就万事大吉。
正解这个结论只在“所有资源都是单实例”时成立。多实例资源下,环只是死锁的必要条件:环里某类资源如果还有空闲实例,等待的一方其实可以拿到资源继续推进,僵局会自然解开。就像四辆车的环路里,如果西车旁边恰好有一条空车道可以绕行,它就不会傻等,环虽然存在,堵死却不会发生。
为何常见:教材举例时为了直观,几乎都用单实例资源画图,看多了容易把“充分”当成“充要”。
误区四个必要条件排第一的就是互斥,那预防死锁自然优先从互斥下手,把它干掉最省事。
正解互斥是很多资源的物理属性,打印机不可能同时打印两份文件,强行共享只会制造数据混乱。实际工程中,“破坏互斥”应用面非常窄,通常只能靠假脱机这类“中间缓冲”间接实现;工业界真正常用的是破坏循环等待的资源编号法,因为它实现简单、对现有系统改动可控。
为何常见:清单顺序带来的“从第一条开始”的直觉,让人们高估了互斥的可改造性。
误区银行家算法能避免死锁,它应该和四种预防策略一样,都属于“预防”范畴吧?
正解银行家算法是死锁避免,不是预防。预防在运行前就用静态规则打破四个条件之一,不需要知道进程的未来需求;避免则在每次资源申请时动态检查系统是否处于安全状态,而它必须提前知道每个进程的最大需求量。打个比方:预防是“出门前把路修成不会堵的结构”,避免是“每次过路口前找交警确认能不能走”。
为何常见:教材把预防、避免、检测恢复放在同一章讲,概念边界被时间线压缩,容易被混为一谈。
误区既然占有并等待是死锁的元凶,那让进程一次性把自己的资源全要齐,不就一劳永逸了吗?
正解预分配确实能断掉“占着等”,但代价是资源利用率急剧下降:进程可能只用打印机十分钟,却要独占它整个生命周期。更隐蔽的是饥饿问题——低优先级进程可能永远凑不齐整套资源,被活活“饿死”。所以现实中预分配只用在事务短、需求明确的场景,而不是银弹。
为何常见:只盯着“死锁消除”这一个目标,忽略了系统整体效率和公平性,是初学者常见的单维度思维。
面试题
Q1. 下列哪一项属于死锁产生的必要条件?
- A.抢占式调度、时间片轮转、优先级反转
- B.互斥条件、占有并等待、不可剥夺、循环等待← 正确答案
- C.共享内存、信号量、管程
- D.同步、互斥、通信、并发
答案:B。四个必要条件缺一不可,就像四辆车在路口僵住需要“单车道、占道、不倒车、围成圈”四个条件同时满足。A 是调度概念,C 是同步工具,D 是并发系统的一般特性,都不是死锁的必要条件。
Q2. 破坏“占有并等待”条件通常采用的方法是?
- A.进程启动前一次性申请所有需要的资源← 正确答案
- B.允许进程随时抢占其他进程资源
- C.为每个资源分配唯一编号并按编号递增申请
- D.将独占资源改装成共享资源
答案:A。破坏占有并等待的经典做法是资源预分配:运行前一次性把全部资源要齐,执行期间不再申请,从而杜绝“占着等”的中间态。B 属于破坏不可剥夺,C 属于破坏循环等待,D 属于破坏互斥。
Q3. 在单实例资源的系统中,若资源分配图中存在一个环,则系统?
- A.一定不会发生死锁
- B.一定发生死锁← 正确答案
- C.可能死锁也可能不会,需要银行家算法进一步判断
- D.死锁与否与环无关
答案:B。单实例资源下,资源分配图中有环是死锁的充分必要条件。因为每个资源只有一个实例,环上的每个进程持有的资源都是环内另一个进程唯一需要的资源,谁也不会主动释放。多实例资源下才需要进一步化简判断。
Q4. 关于“破坏不可剥夺”策略,下列说法正确的是?
- A.所有资源都适合剥夺式管理,因为回滚没有成本
- B.CPU 这类状态易保存的资源可以剥夺,打印机这类状态难回滚的资源不适合← 正确答案
- C.剥夺策略不需要保存任何现场信息
- D.不可剥夺条件无法被任何手段破坏
答案:B。剥夺的前提是资源状态可保存、可恢复。CPU 寄存器中断后保存现场即可转给别人;打印机打了一半的文档没法回滚。就像交警能命令汽车倒车,但没法让打了一半的字消失重来。
Q5. 破坏循环等待条件的经典做法是?
- A.运行前一次性申请全部资源
- B.为资源类型线性排序,进程按编号递增顺序申请← 正确答案
- C.允许进程按任意顺序申请资源
- D.将资源改为共享设备
答案:B。资源编号法规定申请顺序严格递增,环路中必然存在一条编号下降的“回头”边,这条边被规则禁止,环自然无法形成,就像规定所有车只能朝大号路口开,永远绕不成圈。
Q6. 以下哪些属于死锁预防策略?
- A.破坏互斥条件← 正确答案
- B.破坏占有并等待条件← 正确答案
- C.银行家算法
- D.鸵鸟算法
答案:AB。预防策略的本质是运行前用静态规则打破四个必要条件之一,因此破坏互斥与破坏占有等待都属于预防。银行家算法是运行期动态检查安全状态的“避免”策略;鸵鸟算法则是“不处理”的态度,两者都不是预防。
Q7. 关于破坏互斥条件,下列说法正确的是?
- A.互斥条件可以轻松消除,比如把打印机改成共享设备
- B.互斥是很多资源的固有属性,通常只能靠假脱机技术间接绕开← 正确答案
- C.破坏互斥是工业界最常用的死锁预防策略
- D.取消互斥后系统不需要任何同步机制了
答案:B。打印机等设备物理上就是独占的,硬改会造成数据混乱,工程上多用假脱机技术让输出先进磁盘队列、再由专门进程统一打印,进程“以为”打印机是共享的。常用预防策略其实是破坏循环等待的编号法。
Q8. 关于死锁预防与死锁避免的区别,以下说法正确的有?
- A.预防用静态约束在运行前打破四条件之一,不需要未来信息← 正确答案
- B.避免在每次资源申请时动态判断系统是否处于安全状态← 正确答案
- C.预防只适用于单实例资源,避免只适用于多实例资源
- D.两者效果完全相同,只是实现方式不同
答案:AB。预防是“事前修路”,用规则强行拆掉死锁条件;避免是“事中看路”,依赖银行家算法等动态安全检测,且需要知道每个进程的最大需求。C 与 D 的限定和说法都是错误的。
Q9. 某系统采用资源顺序编号法(编号 1、2、3),进程 A 已持有资源 2,现在想申请资源 1。按“递增顺序申请”的规则,正确的做法是?
- A.直接申请资源 1,因为当前资源空闲
- B.必须先释放资源 2,再申请资源 1,避免反向持有← 正确答案
- C.同时申请资源 1 和资源 2
- D.修改资源 1 的编号让它大于资源 2
答案:B。编号法要求申请方向只能由小到大。持有 2 号却想要 1 号属于“回头”申请,必须先把 2 号释放掉再申请 1 号。这套规则就是靠禁止反向申请来杜绝环路,就像规定车辆只能往大号路口开。
Q10. 关于资源分配图与死锁的关系,以下说法正确的有?
- A.单实例资源系统中,存在环路是死锁的充分必要条件← 正确答案
- B.多实例资源系统中,存在环路是死锁的必要条件← 正确答案
- C.只要存在环路就一定死锁,与资源实例数量无关
- D.资源分配图无环时,系统一定不会发生死锁← 正确答案
答案:ABD。单实例下环等价于死锁;多实例下环只是必要条件,因为环内可能有空闲实例化解僵局;无环则说明没有循环等待,死锁不可能成立。C 的错误在于忽略了多实例资源的“救场”能力,就像环路里还有一条空车道可绕行。