golang interview
目录
hhhhh
一个月学习计划
|
|
spin-lock
Spinlock(自旋锁)详解
1. 基本概念
Spinlock(自旋锁)是一种 忙等待(Busy-Waiting)锁,当线程尝试获取锁时,如果锁已被占用,线程不会进入睡眠状态,而是持续循环检查锁的状态,直到锁被释放。
2. 工作原理
- 获取锁:线程通过原子操作(如
CAS, Compare-And-Swap
)尝试获取锁。 - 锁被占用:如果锁已被占用,线程会在一个循环中不断检查锁是否可用(自旋)。
- 锁释放:持有锁的线程释放锁后,等待的线程立即获取锁并继续执行。
伪代码示例(CAS 实现):
|
|
3. 特点
特点 | 说明 |
---|---|
不放弃 CPU | 线程不会进入睡眠状态,而是占用 CPU 循环等待。 |
低延迟 | 锁释放后能立即获取,无线程唤醒开销(适合短临界区)。 |
高 CPU 占用 | 自旋时会空转 CPU,可能浪费资源(长时间等待不适用)。 |
无调度介入 | 不涉及内核态切换,纯用户态操作(对比互斥锁需要 OS 介入)。 |
4. 适用场景
- 短临界区:锁持有时间极短(如几个 CPU 周期)。
- 例如:内核中断处理、无竞争场景下的快速路径(fast-path)。
- 多核 CPU:单核 CPU 上自旋无意义(会占用唯一 CPU)。
- 避免上下文切换:对延迟敏感的场景(如高性能计算、内核同步)。
5. 不适用场景
- 长临界区:自旋会浪费 CPU 时间,应改用阻塞锁(如
mutex
)。 - 单核 CPU:自旋会阻止锁持有者运行,导致死锁。
6. 改进变种
- 自适应自旋锁(Adaptive Spinlock):
根据历史等待时间动态选择自旋或阻塞(如 Java 的synchronized
)。 - Ticket Spinlock:
按请求顺序分配锁,避免饥饿(如 Linux 内核的ticket_spinlock
)。
7. 代码示例(Go 实现)
|
|
8. 与互斥锁(Mutex)对比
特性 | Spinlock | Mutex |
---|---|---|
等待方式 | 忙等待(CPU 空转) | 阻塞(线程休眠) |
开销 | 低(无上下文切换) | 高(需 OS 调度) |
适用场景 | 短临界区、多核 | 长临界区、单核/多核 |
实现级别 | 用户态 | 内核态介入 |
9. 注意事项
- CPU 浪费:长时间自旋会显著增加 CPU 使用率。
- 优先级反转:低优先级线程持有锁时,高优先级线程自旋可能导致死锁。
- Go 的特殊性:Go 的
runtime
对自旋有优化(如混合自旋+阻塞的sync.Mutex
)。
10. 经典应用
- Linux 内核:短临界区同步(如中断处理)。
- 并发数据结构:无锁(lock-free)算法的后备方案。
- 高性能计算:避免线程休眠的开销(如内存分配器)。
如果需要进一步探讨(如与 futex
的结合、性能测试等),可以继续深入!