SpinLock (TTAS) C-A-S 自旋锁实现原理
引用
- SpinLock.h
- SpinLock.cpp
⚙️ 核心结构解析

TTASLock 工作原理
Test-and-Test-and-Set (TTAS) 算法流程:
- 初次测试:快速检查锁状态
- 二次测试:执行原子CAS操作
- 自旋循环:失败后重试
线程 内存位置(atomic _) 读取锁状态 CAS(0→1) 获取锁成功 返回失败/继续自旋 alt [CAS成功] [CAS失败] 等待/重试 alt [锁空闲(0)] [锁忙(1)] 线程 内存位置(atomic _)
⚡ 完整工作流程
Yes
No
Yes
No
超时
线程请求锁
锁状态=0?
执行CAS操作
等待/重试
CAS成功?
进入临界区
退避延迟
执行关键代码
释放锁
设置锁=0
结束
获取失败
⚠️ 致命缺陷:双核死锁场景
核心2
核心1
核心0
持有锁
被抢占
自旋等待
自旋等待
无法调度核心0
100% CPU占用
100% CPU占用
高优先级线程
高优先级线程
共享锁
低优先级线程
操作系统
CPU1
CPU2
死锁形成过程:
- 核心0:低优先级线程持有锁
- 核心1/2:高优先级线程自旋等待
- 核心0被抢占,无法调度
- 高优先级线程持续占用CPU
- 系统资源耗尽 → 内核崩溃
应用场景评估
场景 |
适用性 |
风险等级 |
说明 |
短临界区操作 |
★★★★★ |
低 |
<1000 CPU周期 |
非抢占式内核 |
★★★★☆ |
中 |
需确保持有者不被中断 |
NUMA系统 |
★★☆☆☆ |
高 |
跨节点访问延迟高 |
多核实时系统 |
★☆☆☆☆ |
极高 |
优先级反转风险 |
用户空间程序 |
★★★☆☆ |
中 |
需配合yield/pause指令 |
⚠️ 主要局限性
-
CPU资源浪费
- 自旋线程持续占用CPU核心
- 无法执行其他任务
- 功耗显著增加
-
优先级反转问题
等待
运行
持有锁
调度MP
高优先级线程
锁
中优先级线程
OS
低优先级线程
- 高优先级线程因锁阻塞
- 中优先级线程抢占CPU
- 低优先级线程无法运行释放锁
-
缓存颠簸
- 多核同时访问同一缓存行
- 总线带宽饱和
- 性能断崖式下降
-
无公平性保障
️ 优化方案
-
指数退避策略
尝试获取锁
失败
等待 1μs
等待 2μs
等待 4μs
...
-
MCS锁实现
- 每个线程在本地自旋
- 避免全局缓存行竞争
- 实现先到先服务公平性
-
混合锁机制
if (spin_count < threshold) {
} else {
}
-
指令优化
- 插入
pause
指令(x86)
- 降低CPU功耗
- 减少缓存竞争
关键结论:在锁持有时间小于两次上下文切换开销(约1-2μs)时,TTAS自旋锁性能最优;否则应改用互斥锁或其他同步机制。