人人都能学编程
English 开始学习
核心 · 操作系统第 5 课 / 共 6 课免费阅读

死锁 · 操作系统

两个程序怎么会永远互相等待,卡死在那里?

课程目录

为什么需要它

死锁会让一个程序、一个数据库,甚至整个服务冻住,不报错、也不占 CPU:大家都在礼貌地等待。数据库会检测死锁并杀掉其中一个事务;在你自己的代码里,要靠设计来预防。认得出这个模式,能省下好几天的调试。

直觉理解

两个朋友,一支笔、一个本子。Ann 拿着笔,等本子;Ben 拿着本子,等笔。谁都不放手。

画一张等待图:从每个在等待的进程,画一个箭头指向持有它所需资源的进程。死锁就是这张图里的一个环。

死锁需要四个条件同时成立:资源独占、拿着不放还等别的、不能强行拿走、循环等待。打破任何一个就行。常用办法:永远按同一个全局顺序获取锁(先笔后本子),这样就形不成环。

定义

死锁 Deadlock

A set of processes each waiting for a resource held by another in the set, so none can proceed.
一组进程中,每个都在等另一个持有的资源,结果谁都无法继续。

锁顺序

A rule that every thread acquires locks in the same fixed order. It removes circular waits.
规定所有线程都按同一个固定顺序获取锁。它能消除循环等待。

示例

找出环

动手试试

waits[p] 是 p 在等的那个进程。沿着箭头走;回到已经在路径上的进程,就是死锁。

def deadlocked(waits):
    for start in waits:
        seen = []
        p = start
        while p in waits:
            if p in seen:
                return seen[seen.index(p):]
            seen.append(p)
            p = waits[p]
    return None

print(deadlocked({"A": "B", "B": "C", "C": "A"}))
print(deadlocked({"A": "B", "B": "C"}))
逐步执行
  1. L5只要当前进程还在等别人,就继续往下走。
  2. L6回到了这条路径上已经出现过的进程:有环。
  3. L12A 等 B,B 等 C,C 等 A。['A', 'B', 'C']
  4. L13C 没在等,它会完成并释放其他进程。None

常见错误

用 sleep 来修

加个延时只是改变了时机,让 bug 藏起来了。锁的顺序还是错的,负载一高死锁就会回来。

拿着锁去调用外部代码

拿着锁去调用未知代码(回调、网络请求),等于邀请它以错误的顺序去拿别的锁。先把锁放掉。

小测验

1. 线程 1 先锁 `accounts` 再锁 `log`。线程 2 先锁 `log` 再锁 `accounts`。最简单的修法是?

  1. A让两个线程都先锁 accounts,再锁 log
  2. B在线程 2 里加个 sleep
  3. C用更多线程
查看答案

答案:A

统一的全局顺序让循环等待不可能出现。

2. 这段代码会输出什么?

order = {"pen": 1, "book": 2, "lamp": 3}
wanted = ["lamp", "pen", "book"]
print(sorted(wanted, key=order.get))
  1. A
    ['pen', 'book', 'lamp']
  2. B
    ['lamp', 'pen', 'book']
  3. C
    ['book', 'lamp', 'pen']
查看答案

答案:A

把需要的锁按全局排名排序,就得到了获取它们的顺序。

动手练习

3 道练习,分三层:先预测程序的输出,再按新需求改程序,最后从零写一个。代码在浏览器里运行,提交马上判题。

  1. 预测输出谁在等谁?

    准确写出这段程序的输出。

  2. 改需求给锁排序

    lock_plan(needed) 返回一个线程获取锁的顺序。现在它按列出的顺序拿锁,可能导致死锁。用 RANK 里的全局排名,让每个线程都按同样的顺序拿锁。

  3. 从零写检测死锁

    写函数 has_deadlock(holds, wants)。holds 把每个进程映射到它持有的资源;wants 把每个进程映射到它正在等的资源(或 None)。构建「谁在等谁」,有环就返回 True。

在互动课里练这一课

打开互动课,这些例子都能直接改、直接运行,练习题提交马上判题。免费开始,不用注册。

互动课按顺序解锁:前面的课还没学,会先带你到课程地图。

本课术语

术语中文英文定义
Deadlock死锁Processes waiting on each other in a cycle, so none can continue.
Wait-for graph等待图Arrows from each waiting process to the one it waits for.
Lock ordering锁顺序Always taking locks in one fixed order to prevent deadlock.