You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

GCD串行队列dispatch sync与async执行死锁问题咨询

GCD 串行队列两段代码执行差异原理说明

核心前置规则

首先明确几个关键的GCD执行规则,修正概念误区:

  • 串行队列的任务严格遵循 FIFO(先进先出) 执行顺序:前一个任务没有完全执行结束,队列绝对不会开始执行后一个排队的任务。
  • sync 方法的核心特性:阻塞当前调用线程,直到你提交给sync的任务完全执行完成,方法才会返回。
  • 你之前理解的「sync在调用方线程执行任务」存在小误差:GCD会优先选择当前空闲线程执行sync任务,核心约束是「必须等任务执行完再返回」,执行线程不是固定为调用方线程。

代码片段1(正常运行)执行流程

serialQ.sync {
    print(1)
    serialQ.async {
        print(2)
    }
    serialQ.async {
        print(3)
    }
}

完整执行步骤如下:

  1. 假设这段代码是在和serialQ无关的线程(比如主线程)调用的。首先serialQ.sync会把外层的大闭包任务提交到serialQ的队尾,同时阻塞当前调用的主线程,等待外层闭包执行完成再返回。
  2. 此时serialQ是空队列,外层闭包立刻开始执行,首先输出1。
  3. 接下来执行serialQ.async提交print(2)的任务:任务追加到serialQ队尾,async不阻塞当前线程,代码直接往下走。
  4. 再执行serialQ.async提交print(3)的任务:同样追加到serialQ队尾,不阻塞,代码继续往下走。
  5. 此时外层闭包的所有代码执行完成,主线程的阻塞状态解除。
  6. 最后serialQ按照FIFO顺序执行排队的两个异步任务,依次输出2、3,整个流程没有依赖冲突,不会触发死锁。

代码片段2(触发死锁)执行流程

serialQ.async {
    print(1)
    serialQ.async {
        print(2)
    }
    serialQ.sync {
        print(3)
    }
} 

死锁触发逻辑如下:

  1. 首先调用serialQ.async把外层大闭包提交到serialQ队尾,async不阻塞调用线程,直接返回。
  2. 此时serialQ是空队列,外层闭包立刻开始执行,首先输出1。
  3. 接下来执行serialQ.async提交print(2)的任务:任务追加到serialQ队尾,async不阻塞,代码继续往下走。
  4. 触发死锁的关键点:接下来执行serialQ.sync提交print(3)的任务时,当前调用sync的线程,正好就是serialQ用来执行外层闭包的线程:
    • 第一步,sync会把print(3)的任务追加到serialQ的队尾,此时serialQ的排队顺序为:「正在执行的外层闭包任务」→「print(2)任务」→「print(3)任务」。
    • 第二步,sync会立刻阻塞当前serialQ的执行线程,等待print(3)任务执行完成后才会返回。
    • 但串行队列的FIFO规则要求,必须等前一个任务执行完才能执行下一个,现在外层闭包卡在sync调用的位置,永远执行不完,排在后面的print(2)、print(3)任务永远没有机会出队执行。
    • 外层闭包等待print(3)执行完成才能继续,print(3)等待外层闭包执行结束才能出队执行,二者形成循环等待,直接触发死锁。

内容的提问来源于stack exchange,提问作者Samarth Kejriwal

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 03:06:01