1-12.【GCD】GCD 如何保证串行队列的“串行语义”?

3 阅读2分钟

1️⃣ 串行语义的定义

串行队列保证:同一时间最多只有一个任务在执行,任务严格按提交顺序(FIFO)运行。

  • FIFO 顺序:先提交的任务先执行
  • 互斥:不会同时执行多个任务
  • 线程数量不固定 → 可能不同任务跑在不同线程上

2️⃣ 串行语义不是线程绑定

  • 串行队列不要求“只用一条线程”
  • GCD 只保证**“同一时间最多执行一个 block”**
  • 不同任务可以在不同线程上运行,只要不重叠执行就符合串行语义

3️⃣ GCD 内部机制(state machine)

串行队列实现串行语义的核心手段是:

  1. 队列状态

    • isExecuting 或内部标记:当前是否有任务在执行
  2. FIFO 队列存储任务

    • 每个 async / sync 提交的 block 都被封装成任务对象,排入队列尾
  3. 调度逻辑

    当队列空闲(isExecuting == false)时:

    • 取队列头任务
    • 派发给 target queue(可能是 global queue)
    • 设置 isExecuting = true

    当任务完成:

    • isExecuting = false
    • 取下一个任务执行

⚠️ 关键点:串行控制在队列层,而不是线程层


4️⃣ 示例流程

假设串行队列 serial 提交了三个任务 A、B、C:

提交 A -> 队列:[A] -> 空闲,A 派发 -> isExecuting=true
提交 B -> 队列:[B]
提交 C -> 队列:[B,C]
  • 执行 A

  • 完成 A:

    • 更新队列状态
    • 派发 B
  • 执行 B

  • 完成 B:

    • 派发 C
  • 执行 C

  • 完成 C:

    • 队列空闲

始终保证同时只有一个任务执行


5️⃣ Target Queue 的作用

  • 串行队列通常有一个 target queue

    • 可以是全局并发队列(global queue)
    • 也可以是另一个串行队列
  • 串行语义仍然保持,因为 队列层的状态机只允许一个任务被派发

⚠️ 即使 target 是并发队列,也不会破坏串行语义


6️⃣ 对 sync 和 async 的影响

  • async

    • 提交任务后立即返回
    • 队列状态机负责按顺序派发
  • sync

    • 当前线程可能直接执行任务(fast-path)
    • 队列状态机仍然保证同一时间只有一个任务执行

7️⃣ 总结机制(精炼版)

GCD 保证串行语义的核心:

  1. 每个串行队列维护任务队列 + 执行状态

  2. 提交任务:

    • 放入 FIFO 队列
  3. 队列空闲时:

    • 派发队头任务
    • 设置“正在执行”标记
  4. 任务完成:

    • 清除标记
    • 派发下一个任务
  5. 整个过程中,永远只允许一个任务执行

重点:串行语义靠队列内部调度状态机实现,而不是靠线程数量或线程锁。