4-21.【OC】【锁】priority inversion 在自旋锁中是如何产生的?

0 阅读2分钟

在自旋锁(Spinlock)中,优先级反转(Priority Inversion) 并非传统意义上的“多个线程竞争”,而是一种由于“忙等”机制导致的死锁状态

其产生的核心逻辑如下:

1. 产生的前提条件

  • 基于优先级的抢占式调度:系统总是让优先级最高的就绪线程占用 CPU。
  • 自旋(Busy-wait)机制:线程在获取不到锁时,不进入休眠,而是持续占用 CPU 循环检查锁的状态。
  • 缺乏优先级继承(Priority Inheritance) :自旋锁是底层的同步原语,内核不会因为高优先级线程在等待而临时提升持有锁线程的优先级。

2. 演变过程:从竞争到死锁

我们可以通过三个角色来还原现场:线程 L(低优先级)线程 H(高优先级)自旋锁 Lock

  1. 持有锁:低优先级线程 L 获取了 OSSpinLock,进入临界区开始执行代码。

  2. 抢占:此时,高优先级线程 H 变为就绪状态。由于 H 优先级更高,操作系统内核立即强行剥夺 L 的 CPU 时间片,转而执行 H。

  3. 自旋等待:线程 H 执行过程中尝试获取同一个 OSSpinLock。发现锁被占用,于是 H 开始原地自旋(消耗 CPU 循环检测)

  4. 因果断裂(死锁形成)

    • H 层面:H 正在 CPU 上飞速运转,调度器认为 H 任务紧急且繁忙,因此不断给 H 分配 CPU 时间。
    • L 层面:由于 CPU 一直被 H 占用,L 无法获得任何执行机会。
    • 死结:L 不执行就无法释放锁,L 不释放锁 H 就永远在自旋。

3. 为什么互斥锁(Mutex)能避开?

与自旋锁不同,互斥锁或信号量在获取不到锁时会主动让出 CPU

  • 休眠:当高优先级线程 H 发现互斥锁被占用时,它会进入休眠状态(Wait)。
  • 让权:一旦 H 休眠,它就不再占用 CPU。调度器此时会将 CPU 分配给目前优先级最高的就绪线程,即低优先级的 L。
  • 释放:L 获得 CPU 后完成任务并释放锁,随后唤醒 H。

总结:自旋锁的“自私”导致了毁灭

自旋锁的设计初衷是假设“临界区非常短,稍等片刻就能拿到”。但在 iOS 这种严格区分优先级的系统中,高优先级的“稍等片刻”(自旋)恰恰剥夺了低优先级线程“把锁还回来”的唯一机会。

正如苹果工程师在讨论 OSSpinLock 废弃时所言:自旋锁在高优先级线程看来是一个永远无法满足的条件。