1️⃣ GCD 内部缓解机制:QoS 继承
1.1 原理
-
问题来源:
- 高 QoS 任务等待低 QoS 队列中的任务或锁
- 如果低 QoS 任务占用线程 → 高 QoS 任务被延迟
- 典型场景:串行队列 + 低 QoS 在前 + 高 QoS 在后
-
GCD 解决方案:
- 高 QoS 任务提交到队列时,系统检测 任务阻塞
- 临时提升低 QoS 任务到高 QoS
- 保证低 QoS 任务尽快完成 → 高 QoS 任务继续执行
1.2 示例
let serialQueue = DispatchQueue(label: "serialQueue", qos: .utility)
serialQueue.async(qos: .background) {
Thread.sleep(forTimeInterval: 5) // 低 QoS 任务
}
serialQueue.async(qos: .userInitiated) {
print("高 QoS 任务执行")
}
- 低 QoS 任务在前,正常会阻塞高 QoS
- GCD 会临时提升低 QoS 任务到
.userInitiated→ 缩短高 QoS 阻塞时间
2️⃣ 开发者可以做的优化
2.1 避免串行队列中高低 QoS 混用
-
串行队列顺序固定,低 QoS 在前会阻塞高 QoS
-
建议:
- 高 QoS 任务单独使用高 QoS 队列
- 低 QoS 任务使用后台队列
let highQueue = DispatchQueue(label: "highQueue", qos: .userInitiated)
let lowQueue = DispatchQueue(label: "lowQueue", qos: .background)
2.2 使用异步调度
async提交任务不会阻塞调用线程- 可以减少高 QoS 任务等待低 QoS 队列任务的时间
serialQueue.async {
// 任务不会阻塞调用线程
}
- 避免在高 QoS 任务内调用
sync到低 QoS 队列 → 避免优先级反转
2.3 避免长时间阻塞高 QoS 线程
-
高 QoS 线程上执行耗时任务 → 阻塞自己或其他高 QoS 任务
-
优化:
- CPU 密集型 / 耗时操作 → 移到低或中等 QoS 队列
- 使用
DispatchWorkItem或后台队列处理
2.4 对共享资源使用 GCD 原语
- 串行队列或Dispatch Barrier管理共享资源
- 避免高 QoS 任务被低 QoS 任务持有锁阻塞
let concurrentQueue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
concurrentQueue.async(flags: .barrier) {
// 独占访问共享资源
}
- 可以保证高 QoS 任务访问共享资源时不会被低 QoS 长时间阻塞
2.5 合理拆分任务
- 避免一个低 QoS 大任务占用串行队列过久
- 拆成多个小任务,让系统调度机会更多 → 高 QoS 任务不会被长时间阻塞
3️⃣ 总结
| 方法 | GCD/系统机制 | 开发者措施 |
|---|---|---|
| 缓解优先级反转 | QoS 继承:临时提升低 QoS 任务 | 避免串行队列高低 QoS 混用 |
| 异步 vs 同步 | async 不阻塞调用线程 | 避免高 QoS sync 调用低 QoS 队列 |
| 阻塞控制 | 高 QoS 阻塞会影响系统响应 | 长耗时任务移到低 QoS 队列 |
| 共享资源 | 串行队列 / barrier 保证独占 | 避免高 QoS 等待低 QoS 持有的锁 |
| 任务拆分 | 系统调度机会更多 | 将大任务拆成小任务减少阻塞 |
💡 核心思想:GCD 会临时提升低 QoS 任务优先级,但开发者仍需合理划分任务、队列和资源使用,才能最小化优先级反转对性能的影响。