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

