与 OSSpinlock类似,使用 dispatch_semaphore 容易造成优先级反转:
- 此 API 没有记录当前持有信号量的线程,所以有高优先级的线程在等待锁时,内核无法知道该提高哪个线程的调试优先级(QoS)
- 如果锁持有者优先级比其他线程低,高优先级的等待线程将一直等待
高效率的线程同步有两个关键点:
- 不忙等
- 记录持有者
自旋锁是两点都不符合,dispatch_semaphore 是只符合不忙等。
原理说明
下面先介绍一下 iOS 平台上的 QoS 概念和优先级反转避免机制,最后再说明为什么 dispatch_semaphore 不能避免优先级反转。
1. QoS 传递
QoS(Quality of Service),用来指示某任务或队列的运行优先级。
- iOS 系统主要使用以下两种机制来在不同线程(或 queue)间传递 QoS:
- 机制1:dispatch_async
- 机制2:基于 XPC 的进程间通信(IPC)
- 系统的 QoS 传递规则比较复杂,主要参考以下信息:
- 当前线程的 QoS
- 如果是使用 dispatch_block_create() 方法生成的 dispatch_block,则考虑生成 block 时所调用的参数
- dispatch_async 或 IPC 的目标 queue 或线程的 QoS
调度程序会根据这些信息决定 block 以什么优先级运行。
如果没有其他线程同步地等待此 block,则 block 就按上面所说的优先级来运行。
如果出现了线程间同步等待的情况,则调度程序会根据情况调整线程的运行优先级。
2、优先级反转及避免
优先级反转:当前线程在同步地等待某个线程(线程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()
- pthread mutex、os_unfair_lock、以及基于这二者实现的其他上层 API
使用以上这些 API 能够在发生优先级反转时使系统启用优先级反转避免机制。
3、dispatch_semaphroe 不能避免优先级反转的原因
- semaphore 不是一个异步方法,所以它没有 QoS 的概念
- 在调用 dispatch_semaphore_wait() 时,系统不知道哪个线程会调用 dispatch_semaphore_signal() 方法,所以系统无法知道 owner 信息
dispatch_group 跟 semaphore 类似,在调用 enter() 方法时,无法预知谁会调用 leave(),所以系统也无法知道其 owner 是谁
网友评论