自旋锁,就是在尝试获取锁失败时,不直接挂起,而是一直循环尝试获取锁。这在一些锁冲突较小且占用锁时间非常短的场景下非常有用,因为它可以减少相应的系统调用。
接下来我们就使用 Rust 来实现一个自己的自旋锁。
基础版
首先是最基础的版本,我们在 lock
的时候,如果失败了,就一直循环尝试,直到成功获取锁:
1 | pub struct SpinLock { |
在上述实现中,我们定义了结果 SpinLock
,它包含一个
value
和原子变量 locked
。
在 lock
方法中,我们尝试对 locked
原子变量进行 swap
为 true
的操作,swap
会返回交换之前的值,如果是
false
,那就说明抢锁成功了,这个时候 lock
就成功返回,否则,则调用 std::hint::spin_loop()
进行自选,在下一次 while
循环中再尝试获取锁。
在 unlock
方法中,我们只需要将 locked
设置为 false
即可。
示例图如下:
这里有 2 个需要关注的点:
std::hint::spin_loop()
会向 CPU 发送特定指令(如 x86 的 pause 或 ARM 的 yield),提示当前处于忙等待状态。这允许 CPU 优化执行行为,例如:- 降低功耗:减少自旋期间的计算资源消耗。
- 提升多线程效率:在超线程架构中,避免单个核心的忙等待阻塞其他线程的执行。
- 这里我们内存循序使用了一对
Acquire
和Release
。其中:Acquire
是为了当前线程可以看到其他线程对locked
的写结果,即看到当前locked
真正的值。Release
是为了当前线程对locked
写的结果对其他线程可见,即释放当前locked
真正的值给其他线程。