1-3.【GCD】DispatchQueue 的底层数据结构是什么?

47 阅读3分钟

DispatchQueue 的底层实现是一个高度复杂的 C 语言结构体,定义在 Apple 的开源库 libdispatch(也称为 GCD)中。它并不是一个单一的数组或链表,而是一个分层的、高度优化的对象。

其最核心的底层数据结构是 dispatch_queue_t,在源码层面,它实际上是指向 struct dispatch_queue_s 的指针。


1. 核心结构体组成

一个 dispatch_queue_s 结构体包含了管理任务和状态的关键字段:

  • dq_items_head / dq_items_tail

    这是队列的本质——一个 基于链表的 FIFO(先进先出)任务队列。当你调用 async 时,任务会被封装成 dispatch_continuation_t 节点并挂载到这个链表上。

  • dq_state

    这是一个 64 位的原子变量,存储了队列的当前状态,包括:

    • 是否已挂起(Suspended)。
    • 是否有活跃的任务(Width/Concurrent count)。
    • 是否正在被某个线程处理(Dirty bits)。
  • dq_targetq (目标队列)

    这是 GCD 最精妙的设计。每个用户创建的队列都有一个指向“目标队列”的指针,最终指向系统定义的 Root Queues(全局并发队列) 。这种层级结构决定了任务最终在哪组线程池中执行。


2. 任务单元:dispatch_continuation_s

你提交的每个闭包(Closure)在底层都会被包装成一个 Continuation 结构体。它包含:

  • 函数指针(指向你的 Swift/OC 代码块)。
  • 上下文数据(Context,即闭包捕获的变量)。
  • 优先级信息(QoS)。

3. 并发控制:位图与原子操作

为了保证性能,GCD 在管理队列状态时极少使用传统的 mutex 锁,而是大量使用 原子操作(Atomic Operations)

  • 串行队列:其 dq_width 为 1。底层通过 dq_state 的原子竞争来确保同一时间只有一个线程能“认领”该队列的任务。
  • 并发队列:其 dq_width 很大。它允许从链表中同时取出多个任务并分发给不同的线程。

4. 与内核的纽带:vnodekevent

在更底层的 libdispatch 内部,它使用 dispatch_object_s 作为基类,构建了一套模拟面向对象的层次结构。

当队列中有新任务时,GCD 不会不停地轮询。它利用 kevent(内核事件通知机制) 与操作系统内核通信。内核负责监控队列状态,并在有任务且有 CPU 空闲时,唤醒工作线程去消费任务链表。


5. 总结:队列的“骨架”

组件底层数据结构作用
队列对象dispatch_queue_s存储状态、宽度和链表指针。
任务节点dispatch_continuation_s封装用户闭包和执行环境。
状态控制uint64_t (Atomic State)通过位运算实现无锁的状态切换。
分发层级dq_targetq 指针将用户任务向上合并到系统线程池。

💡 深度启发:为什么自定义队列比全局队列慢一点点?

因为自定义队列必须经过一次或多次 dq_targetq 的查找(由用户队列 -> 根队列),而全局队列直接对接工作池。虽然这个开销在现代硬件上可以忽略不计,但这体现了 GCD 层层递进的层级设计。