在 NSOperation 的世界里,优先级决定了谁能“插队”,但它并不是万能的。理解它的运作机制需要区分 队列内优先级 和 系统级资源分配。
1. 优先级是硬约束还是软提示?
NSOperation 的 queuePriority 属性是一个典型的软提示(Soft Hint) 。
调度的真实逻辑:
- 就绪过滤:
OperationQueue首先筛选出所有isReady状态为YES的任务。 - 排序竞争:在这些已经就绪的任务中,队列会参考
queuePriority来决定启动顺序。 - 非绝对执行:它不能保证优先级高的任务一定比优先级低的任务先完成,甚至不能百分之百保证先启动。
为什么是“软”的?
- 依赖关系高于优先级:如果一个高优先级任务依赖于一个低优先级任务,那么低优先级任务必须先执行。此时优先级失效。
- 并发数限制:如果
maxConcurrentOperationCount大于 1,系统可能会同时启动高低优先级的任务。
2. queuePriority 与 QoS (Quality of Service) 的区别
这是最容易混淆的概念。简单来说:queuePriority 决定“先后”,而 QoS 决定“快慢”。
| 特性 | queuePriority (队列优先级) | Quality of Service (QoS) |
|---|---|---|
| 作用范围 | 仅在 同一个队列 内部有效。 | 在 全局系统 范围内有效。 |
| 控制对象 | 控制任务在队列中的入场顺序。 | 控制线程的硬件资源分配(CPU、磁盘、网络)。 |
| 影响维度 | 调度顺序。 | CPU 周期、线程优先级、IO 优先级。 |
| 典型值 | VeryLow, Normal, High 等。 | UserInteractive, Utility, Background 等。 |
形象比喻:
- queuePriority:就像在银行排队,你是 VIP 客户,可以排在普通客户前面(优先进窗口)。
- QoS:就像你进了窗口后,银行给你的办事效率。你是高 QoS,柜员就全力以赴帮你办;你是低 QoS,柜员可能一边喝咖啡一边慢悠悠地处理。
3. 优先级是如何影响调度的?
OperationQueue 内部维护着一个等待执行的池子。
- 入队:任务进入池子,标记为
Pending。 - 检查依赖:一旦任务的依赖项全部完成,该任务被标记为
Ready。 - 排序触发:当线程池有空闲时,调度器扫描所有
Ready任务。它会挑选queuePriority最高的那个交给 GCD 的底层线程池。 - 动态性:如果在执行过程中,有一个更高优先级的任务突然变为
Ready,且此时还有并发名额,它会立即被启动,而不会等待正在运行的低优先级任务结束。
4. 优先级反转 (Priority Inversion)
这是使用优先级时需要警惕的风险:如果一个 High 优先级的任务依赖于一个 Low 优先级的任务,而队列中又有大量的 Normal 任务。
- 结果:
Low任务因为优先级太低,一直抢不到资源执行,导致High任务被无限期阻塞。 - 系统的应对:现代 iOS 系统会自动进行 “优先级提升” ,暂时提高那个被依赖的
Low任务的优先级,确保整条链条能跑通。
总结:使用建议
- 不要过度依赖优先级:复杂逻辑应通过
addDependency:解决。 - 优先设置 QoS:如果你想让某个后台任务不耗电、不发热,设置
qualityOfService = .background比调整queuePriority有效得多。 - UI 相关设为高:只有那些直接影响用户体验的操作(如加载当前屏幕的图片)才建议设为
.high。