“递归锁是否允许同一线程重复获取、非所有者释放如何处理、等待队列是否 FIFO,以及是否提供有界等待,都是具体接口的契约,不属于“mutex”一词自动携带的性质。普通 mutex 通常是阻塞式…”
形式陈述 ​
自旋锁是互斥锁的一类实现:调用者获取失败后不阻塞睡眠,而是在处理器上重复检查共享状态。最简单的 test-and-set(TAS)锁维护原子布尔量 held,其核心协议是:
lock():
while test_and_set(held, acquire) == true:
pause
unlock():
store(held, false, release)
test_and_set 必须是不可分割的读改写:它返回旧值并把 held 写为真。只有观察到旧值为假的调用取得锁,因此成功 RMW 是获取的线性化点;release store 是释放的线性化点。若把它拆成普通 load 与 store,两个线程可能同时读到假再分别写真,互斥立即失效。
acquire 与 release 不是性能注解,而是协议正确性的一部分。成功获取需要阻止临界区读写越过入口向前移动,释放需要保证临界区修改在锁重新可用前按模型要求发布。失败轮询往往可以使用更弱的内存序,但具体选择必须由目标内存模型证明;单纯保证对 held 的原子性,不足以保证受保护数据的可见性。
TAS 锁不规定等待顺序。ticket lock 用两个单调计数器 next 与 serving 改善这一点:线程以原子 fetch-and-increment 取得票号 serving=t,释放时递增 serving。在票号不发生歧义、持锁者最终释放且等待线程得到执行的模型下,请求按票号顺序服务,提供 FIFO 式有界等待。这里的顺序是锁请求队列顺序,不是操作系统的实时公平或优先级承诺。
无竞争获取只需常数个原子步骤;有竞争时,一个调用可能自旋任意久并消耗无界处理器步骤,因此它不是wait-free 操作。MCS、CLH 等队列锁让线程主要轮询本地或前驱节点,可减少所有核心反复争抢同一缓存行;它们改变扩展性,不改变“忙等获取”的语义中心。
直觉 ​
自旋相当于站在门口盯着钥匙,而阻塞 mutex 是先登记、离开队列,等通知再回来。若持锁者正在另一颗核心上运行,且临界区只剩几十条指令,原地等待可能避开一次昂贵的睡眠与唤醒。自旋的收益来自“等待很短且持锁者确实能并行推进”这一具体条件,并非来自锁名本身。
轮询的代价不止是执行循环。多个核心对同一锁字做写型 RMW 会使缓存行反复失效,在总线和一致性协议上形成热点。ticket lock 固定了服务顺序,却让所有等待者读取同一个 serving;释放时该缓存行仍需传播给大量核心,所以公平改进不自动带来高扩展性。
例子与边界 ​
内核在多核机器上保护一段极短、不会睡眠的数据结构更新时,可以使用自旋锁。核心 A 持锁递增运行队列计数,核心 B 自旋数百纳秒后立即接手;若改为阻塞,调度路径本身可能比等待更长。这个判断依赖临界区短小、持锁期间不执行可能睡眠的操作,并且持锁者没有被长期抢占。
在单核不可抢占环境中,等待者一旦开始自旋,持锁线程便可能没有机会运行和释放锁;忙等只是在烧掉全部 CPU 时间。即使是多核,持锁者若被操作系统换出,其他核心也可能持续轮询一个短期内不可能变化的状态。中断上下文还需遵守平台的中断屏蔽与锁层级规则,否则被中断代码持有的锁可能由中断处理器再次请求,形成自死锁。
自旋循环中绝不能执行可能睡眠的阻塞调用:当前线程既占着锁又让出处理器,等待者仍在其他核心消耗算力,而唤醒路径还可能反向需要同一把锁。混合型 mutex 可以先短暂自旋,未等到再阻塞;这是针对负载的工程折中,不意味着所有自旋锁都应内置复杂的自适应策略。
推论与应用 ​
自旋锁展示了互斥安全、等待策略和公平性必须分层描述。TAS 依靠单个原子读改写建立互斥,却允许饥饿;ticket lock 改善请求顺序,却仍可能因持锁者停止而失去整体进展。选择实现时应同时检查调度模型、临界区是否可抢占、缓存拓扑和内存序,而不能用一次低竞争基准替代这些假设。
它也说明“无锁算法”与“代码里没有 mutex”不是一回事。自旋锁虽然不让等待线程睡眠,算法进展仍依赖特定持锁者释放,因此不满足lock-free 进展。术语中的 lock-free 描述系统级完成保证,而不是 CPU 是否正在执行轮询指令。
参考资料
- John M. Mellor-Crummey and Michael L. Scott, “Algorithms for Scalable Synchronization on Shared-Memory Multiprocessors,” ACM TOCS 9(1), 1991, pp. 21–65。
- Maurice Herlihy and Nir Shavit, The Art of Multiprocessor Programming, rev. 1st ed., Morgan Kaufmann, 2012,Ch. 7。
- Linux Kernel Documentation, “Locking” 与 “Lock types and their rules”。