第3章 并发、同步与死锁¶
并发让系统在等待 I/O 时继续做别的工作,也让多核 CPU 真正同时执行多个任务。但只要多个执行流共享可变状态,程序结果就可能依赖难以复现的执行时序。
从“交错”理解并发¶
两个线程各自的代码顺序没有变化,但它们的指令可以按许多方式交错。正确的并发程序必须保证:在所有允许的交错中,关键不变量都成立。
最小例子是两个线程同时执行 counter++。这通常包含读取、加一和写回三个步骤;如果两者都读到旧值,最终只增加一次。问题不在算术,而在复合操作缺少原子性。
小结目录¶
- 竞态、原子性与锁:从丢失更新推导临界区、互斥锁、自旋锁、原子指令和内存可见性。
- 信号量与条件变量:区分“保护共享状态”和“等待状态变化”,给出有界缓冲区最小实现。
- 经典同步问题:用生产者—消费者、读者—写者和哲学家问题训练建模与不变量分析。
- 死锁、检测与恢复:理解死锁必要条件、资源分配图、预防、避免、检测和恢复。
工具选择的直觉¶
| 需求 | 常见工具 |
|---|---|
| 一个时刻只允许一个线程修改状态 | 互斥锁 |
| 临界区极短且不能睡眠 | 自旋锁或原子操作 |
| 等待“队列非空”等条件成立 | 条件变量 |
| 表示有限数量的同类资源 | 计数信号量 |
| 一次性发布初始化结果 | once、屏障或安全发布机制 |
工具名称不是重点。重点是明确共享状态、不变量、谁修改状态、谁等待条件,以及等待和唤醒是否都在同一同步协议中。
学完应能回答¶
- 数据竞态、竞态条件和原子性违反有何区别?
- 为什么检查条件后必须在循环中等待条件变量?
- 信号量为什么既能表达互斥,也能表达资源数量?
- 哪四个条件同时成立时才可能死锁?
- 避免死锁与避免饥饿为什么不是同一件事?