与 OSSpinlock相似,使用 dispatch_semaphore 容易形成优先级反转:html
此 API 没有记录当前持有信号量的线程,因此有高优先级的线程在等待锁时,内核没法知道该提升哪一个线程的调试优先级(QoS)markdown
若是锁持有者优先级比其余线程低,高优先级的等待线程将一直等待app
高效率的线程同步有两个关键点:异步
- 不忙等
- 记录持有者
自旋锁是两点都不符合,dispatch_semaphore 是只符合不忙等。async
下面先介绍一下 iOS 平台上的 QoS 概念和优先级反转避免机制,最后再说明为何 dispatch_semaphore 不能避免优先级反转。ide
QoS(Quality of Service),用来指示某任务或队列的运行优先级。oop
iOS 系统主要使用如下两种机制来在不一样线程(或 queue)间传递 QoS:ui
机制1:dispatch_asyncspa
机制2:基于 XPC 的进程间通讯(IPC)线程
系统的 QoS 传递规则比较复杂,主要参考如下信息:
当前线程的 QoS
若是是使用 dispatch_block_create() 方法生成的 dispatch_block,则考虑生成 block 时所调用的参数
dispatch_async 或 IPC 的目标 queue 或线程的 QoS
调度程序会根据这些信息决定 block 以什么优先级运行。具体用法请参见QoS使用官方文档。
若是没有其余线程同步地等待此 block,则 block 就按上面所说的优先级来运行。
若是出现了线程间同步等待的状况,则调度程序会根据状况调整线程的运行优先级。
优先级反转:当前线程在同步地等待某个线程(线程1)完成某项操做,而当前线程的优先级比线程1的优先级高。
优先级反转避免机制(Priority inversion avoidance):若是当前线程因等待某线程(线程1)上正在进行的操做(如 block1)而受阻,而系统知道 block1 所在的目标线程(owner),系统会经过提升相关线程的优先级来解决优先级反转的问题。
若是系统不知道 block1 所在目标线程,则没法知道应该提升谁的优先级,也就没法解决反转问题
记录了持有者信息(owner)的系统 API 以下:
pthread mutex、os_unfair_lock、以及基于这两者实现的其余上层 API
dispatch_once 的实现是基于 os_unfair_lock 的
NSLock、NSRecursiveLock、@synchronized 等的实现是基于 pthread mutex 的
dispatch_sync()
xpc_connection_send_with_message_sync()
使用以上这些 API 可以在发生优先级反转时使系统启用优先级反转避免机制。
semaphore 不是一个异步方法,因此它没有 QoS 的概念
在调用 dispatch_semaphore_wait() 时,系统不知道哪一个线程会调用 dispatch_semaphore_signal() 方法,因此系统没法知道 owner 信息
dispatch_group 跟 semaphore 相似,在调用 enter() 方法时,没法预知谁会调用 leave(),因此系统也没法知道其 owner 是谁