Appearance
锁
考情分析
锁作为互斥的具体实现机制,408 真题中偶有涉及。属于 🔥 中低频考点。
前面的 TSL、Swap 等硬件方法都有忙等待的问题。锁是对这些底层机制的封装——自旋锁选择忙等,互斥锁选择阻塞,各有适用场景。
锁的概念
锁是实现互斥的最直接方式。一个锁只有两种状态:
- 可用(unlocked):没有进程持有锁
- 已占用(locked):某个进程正持有锁
acquire(lock); // 获取锁(进入区)
临界区;
release(lock); // 释放锁(退出区)自旋锁(Spinlock)
自旋锁使用忙等待(busy waiting):获取锁失败时,在循环中反复检查锁的状态。就像在洗手间门口不停拧门把手试——如果里面的人马上出来还好,要是等很久就纯粹浪费体力。
c
// 基于 TSL 实现的自旋锁
void acquire(lock) {
while (TestAndSet(&lock)); // 一直循环直到获取锁
}
void release(lock) {
lock = false;
}| 优点 | 缺点 |
|---|---|
| 实现简单 | 忙等待浪费 CPU 时间 |
| 不需要上下文切换 | 不满足让权等待原则 |
| 适合临界区很短的情况 | 不适合单处理机 |
互斥锁(Mutex Lock)
互斥锁使用阻塞/唤醒机制:获取锁失败时,进程被阻塞(让出 CPU),锁释放时唤醒等待的进程。
void acquire(lock) {
if (lock 已被占用) {
将自己加入等待队列;
阻塞(block);
}
lock = 已占用;
}
void release(lock) {
if (等待队列非空) {
唤醒(wakeup)一个等待进程;
}
lock = 可用;
}| 优点 | 缺点 |
|---|---|
| 满足让权等待 | 阻塞和唤醒有上下文切换开销 |
| 不浪费 CPU | 实现较复杂 |
| 适合各种场景 | — |
自旋锁 vs 互斥锁
| 特性 | 自旋锁 | 互斥锁 |
|---|---|---|
| 等待方式 | 忙等待(循环检查) | 阻塞等待(让出 CPU) |
| CPU 开销 | 等待期间持续占用 CPU | 等待期间不占 CPU |
| 上下文切换 | 无 | 有 |
| 适用场景 | 临界区很短、多处理机 | 临界区较长、单处理机 |
| 让权等待 | 不满足 | 满足 |
考研高频考点
- 🔥🔥 自旋锁和互斥锁的区别(忙等待 vs 阻塞)
- 🔥 自旋锁不适合单处理机的原因
- 🔥 互斥锁满足让权等待
锁只能解决互斥问题。当进程之间还有同步需求(先后顺序、资源计数)时,就需要更强大的工具——下一篇看信号量和 PV 操作。