[GCD] Dispatch Queue: cola FIFO, prioridad y tipos de colas

[GCD] Dispatch Queue: FIFO queue, priority, and queue types

A DispatchQueue is the object to which work is sent in GCD. You don’t send threads to it; you send closures (blocks of code), and it is the queue that decides how and when to execute them. DispatchQueue 是 GCD 中用于接收任务的对象。你发送的不是线程,而是闭包(代码块),由队列决定如何以及何时执行它们。

FIFO: Order of start, not completion

FIFO:启动顺序,而非完成顺序

A DispatchQueue is a FIFO (first in, first out) queue: tasks begin executing in the order they were dispatched. This does not imply that they finish in that same order—that depends on whether the queue is serial or concurrent (which will be covered in the next two articles). The only thing guaranteed by FIFO is the order of start. DispatchQueue 是一个 FIFO(先进先出)队列:任务会按照它们被派发的顺序开始执行。但这并不意味着它们会按同样的顺序完成——这取决于队列是串行还是并发的(这将在接下来的两篇文章中介绍)。FIFO 唯一保证的是启动顺序。

Priority: Quality of Service (QoS)

优先级:服务质量 (QoS)

Each dispatched task has an associated priority, expressed as DispatchQoS. The system uses this priority to decide which task to handle first when several compete for CPU: 每个被派发的任务都有一个关联的优先级,表示为 DispatchQoS。当多个任务竞争 CPU 时,系统会使用该优先级来决定优先处理哪个任务:

  • .userInteractive: The highest. It does not mean “runs on main” or “blocks the interface”—UI blocking only occurs if the work runs on the main thread, and that is decided by the queue, not the QoS. It is for work whose delay would make the interface feel frozen (animations, or a background calculation whose result immediately feeds the UI). .userInteractive: 最高优先级。它并不意味着“在主线程运行”或“阻塞界面”——UI 阻塞仅在任务运行于主线程时发生,这是由队列决定的,而非 QoS。它适用于延迟会导致界面卡顿的任务(如动画,或结果需立即反馈给 UI 的后台计算)。
  • .userInitiated: Work that the user is actively waiting for (opening a document, processing the result of a tap). .userInitiated: 用户正在主动等待的任务(如打开文档、处理点击事件的结果)。
  • .default: The default priority when no explicit one is specified. .default: 未指定优先级时的默认值。
  • .utility: Long-running work that does not need an immediate result (downloads, data processing, with a visible progress bar). .utility: 不需要立即获得结果的长时间运行任务(如下载、数据处理,通常带有进度条)。
  • .background: The lowest. Work that the user does not perceive (background synchronization, indexing). .background: 最低优先级。用户感知不到的任务(如后台同步、索引)。

QoS is a relative signal of priority, not a guarantee of speed. The system scheduler uses it to decide, when there are more threads with pending work than available cores, which ones to assign CPU time to first. If all tasks in a process declare .userInteractive, the scheduler loses the information it needs to differentiate them: they all ask for the same level of service, so they end up sharing the same cores by turns (time-slicing), just as they would if they all had .default. The measurable result is that no task finishes earlier for being marked .userInteractive—the animation code that actually needs that priority now competes for CPU against work that didn’t need it, with the same chances of being interrupted. QoS 是优先级的相对信号,而非速度保证。当待处理任务的线程数多于可用核心数时,系统调度器会利用它来决定优先分配 CPU 时间给谁。如果进程中的所有任务都声明为 .userInteractive,调度器就会失去区分它们所需的信息:它们都要求相同的服务级别,因此最终会轮流共享核心(时间片轮转),就像它们都被设为 .default 一样。可衡量的结果是,没有任何任务会因为被标记为 .userInteractive 而提前完成——原本需要该优先级的动画代码现在必须与不需要它的任务竞争 CPU,被中断的概率是一样的。

Queue Types

队列类型

GCD exposes three ways to obtain a DispatchQueue: GCD 提供了三种获取 DispatchQueue 的方式:

// 1. Main queue: serial, linked to the main thread (UI)
// 1. 主队列:串行,绑定到主线程 (UI)
let main = DispatchQueue.main

// 2. Global queues: concurrent, provided by the system, one for each QoS
// 2. 全局队列:并发,由系统提供,每个 QoS 等级一个
let global = DispatchQueue.global(qos: .userInitiated)

// 3. Custom queue: serial by default
// 3. 自定义队列:默认串行
let custom = DispatchQueue(label: "com.miapp.tareas")

// 3b. Custom queue, explicitly made concurrent
// 3b. 自定义队列,显式设为并发
let customConcurrent = DispatchQueue(label: "com.miapp.tareas-concurrentes", attributes: .concurrent)
  • Main queue (DispatchQueue.main): It is serial and always executes on the main thread. Everything that touches the UI must pass through here. 主队列 (DispatchQueue.main): 它是串行的,且始终在主线程执行。所有涉及 UI 的操作都必须通过此队列。
  • Global queues (DispatchQueue.global(qos:)): They are concurrent and managed by the system. They are not created, they are obtained—there is one for each QoS level, and they are shared with the rest of the app (and potentially other system processes). The signature is global(qos: DispatchQoS.QoSClass = .default): the parameter has a default value, so DispatchQueue.global() without arguments does not mean “no QoS”—it is explicitly equivalent to DispatchQueue.global(qos: .default). 全局队列 (DispatchQueue.global(qos:)): 它们是并发的,由系统管理。它们不是被创建的,而是被获取的——每个 QoS 等级对应一个队列,并与应用的其他部分(甚至可能与其他系统进程)共享。其签名为 global(qos: DispatchQoS.QoSClass = .default):该参数有默认值,因此不带参数的 DispatchQueue.global() 并不意味着“没有 QoS”,它显式等同于 DispatchQueue.global(qos: .default)
  • Custom queues (DispatchQueue(label:)): Created by the developer. By default, they are serial; they become concurrent only if attributes: .concurrent is specified. The label does not affect behavior—it serves only to identify the queue in debugging tools like Instruments. 自定义队列 (DispatchQueue(label:)): 由开发者创建。默认情况下是串行的;只有指定 attributes: .concurrent 时才会变为并发。标签 (label) 不会影响行为——它仅用于在 Instruments 等调试工具中标识队列。

Note: In the code above, only the global queue explicitly shows a QoS (qos: .userInitiated). It is not that DispatchQueue.main and DispatchQueue(label:) lack a QoS—DispatchQueue.main always runs with the highest system priority and cannot be reconfigured, and DispatchQueue(label:) does accept a qos: parameter in its full initializer (init(label:qos:attributes:autoreleaseFrequency:target:)), it is simply not necessary yet for what this article covers. How to assign QoS to a custom queue is explained in a later article. 注意:在上述代码中,只有全局队列显式展示了 QoS (qos: .userInitiated)。这并不是说 DispatchQueue.mainDispatchQueue(label:) 没有 QoS——DispatchQueue.main 始终以系统最高优先级运行且无法重新配置,而 DispatchQueue(label:) 在其完整初始化器 (init(label:qos:attributes:autoreleaseFrequency:target:)) 中确实接受 qos: 参数,只是对于本文涵盖的内容而言,目前尚不需要。如何为自定义队列分配 QoS 将在后续文章中解释。

What’s next

接下来

With serial vs. concurrent queues identified, what remains is to see how behavior changes when dispatching work synchronously or asynchronously in each. Those are the next four articles. 在明确了串行与并发队列的区别后,接下来要看的是在每种队列中同步或异步派发任务时,行为会发生怎样的变化。这些内容将在接下来的四篇文章中介绍。

Bibliography

参考资料