4-22.【OC】【锁】os_unfair_lock 如何避免优先级反转?

7 阅读2分钟

os_unfair_lock 是苹果在 iOS 10 中推出的 OSSpinLock 直接替代品。它能够规避优先级反转问题,核心在于它彻底改变了等待机制,并与内核调度器(Mach Kernel)进行了深度联动。

以下是它避免优先级反转的三个关键机制:

1. 从“自旋”转为“休眠”(Wait-set)

OSSpinLock 不同,os_unfair_lock 不是自旋锁,而是一种互斥锁的变体。

  • 不再抢占 CPU:当高优先级线程(H)发现锁被占用时,它不会在 CPU 上死循环(自旋),而是会被内核挂起进入休眠状态
  • 释放 CPU 资源:由于 H 进入了休眠,它不再占用 CPU 时间片。此时,系统调度器可以从容地将 CPU 分配给低优先级线程(L),让 L 有机会执行完临界区代码。

2. 优先级继承(Priority Inheritance)

这是 os_unfair_lock 最核心的“救命稻草”。它通过与 Mach 内核的交互实现了优先级补偿

  • 动态提升:当内核发现一个高优先级线程正在等待 os_unfair_lock 时,它会检查是谁持有了这把锁。
  • 临时提权:如果持有锁的是低优先级线程(L),内核会临时将 L 的优先级提升到与 H 相同的水平
  • 快速放锁:通过提权,L 能够抢占系统中其他中等优先级的线程,确保它能尽快运行并调用 os_unfair_lock_unlock 释放资源。一旦释放,L 的优先级会恢复原状。

3. “非公平”策略(Unfairness)的优势

名字中的 "unfair" 指的是它不保证“先来后到”(即不是 FIFO 队列),这种设计虽然听起来不道德,但对性能和避免反转大有裨益:

  • 减少上下文切换:如果一个线程释放了锁,而另一个线程刚好正在尝试获取锁,系统可能会直接把锁给当前活跃的线程,而不是强行唤醒一个正在休眠的线程。
  • 吞吐量优先:这种策略减少了线程在“休眠-唤醒”之间的切换开销,使得整体处理速度变快,间接减少了锁被长时间持有的概率。

总结对比

特性OSSpinLock (已废弃)os_unfair_lock (推荐)
等待方式忙等 (Busy-wait),持续消耗 CPU休眠 (Sleep),出让 CPU
内核联动有 (与 Mach 调度器高度耦合)
优先级反转会触发死锁通过优先级继承规避
性能极高(但高负载下危险)接近自旋锁的高性能,且安全

如何使用

在 iOS 中,你可以通过以下方式替代旧的锁逻辑:

Objective-C

#import <os/lock.h>

// 1. 初始化
os_unfair_lock_t lock = &(OS_UNFAIR_LOCK_INIT);

// 2. 加锁
os_unfair_lock_lock(lock);

// 3. 临界区代码
// ... 

// 4. 解锁
os_unfair_lock_unlock(lock);