Swift GCD中自定义串行队列async内调用sync引发死锁的原因及全局并发队列无此问题的底层解析
Swift GCD中自定义串行队列async内调用sync引发死锁的原因及全局并发队列无此问题的底层解析
嘿,我来帮你把这个死锁问题拆解得明明白白,其实核心就是GCD里串行队列和并发队列的调度逻辑天差地别~
一、自定义串行队列死锁的来龙去脉
先跟着你的代码走一遍流程,你就能瞬间get到死锁的点:
- 你的
updateUI函数在主线程调用queue.async,把外层的闭包(咱们叫它任务A)扔进了自定义的串行队列里排队。 - 系统给这个串行队列分配了一条线程T1,开始执行任务A。这里要划重点:串行队列同一时间只能执行一个任务,它的任务是按先进先出(FIFO)的顺序来,而且当前执行的任务会“占着”队列的唯一执行位。
- 任务A执行到
queue.sync的时候,麻烦来了:sync是同步阻塞调用——它要求队列立刻执行传入的新闭包(任务B),而且会死死卡住当前的线程T1,必须等任务B执行完才会继续往下走。 - 但现在的情况是:串行队列正被任务A占着,根本腾不出手来执行任务B;而任务A又被
sync堵着,必须等任务B做完才能继续。这就形成了经典的互相等待死锁——你等我完成,我等你腾位置,谁都动不了,程序自然就挂住了。
二、为什么全局并发队列不会出这个问题?
那换成全局并发队列为啥就啥事没有?核心在于并发队列的调度逻辑:
- 全局并发队列是并发队列,它的设计就是允许同时执行多个任务(当然实际能跑多少受系统线程数限制,但逻辑上支持多任务并行)。
- 当你在并发队列的async任务(任务A)里调用
sync时,并发队列不需要等任务A结束,它可以直接拿出另一条空闲线程T2来执行新的任务B。 - 等任务B执行完成后,
sync的阻塞就解除了,任务A可以继续往下走,整个流程完全顺畅,根本不会出现互相卡壳的情况。
三、再补点底层细节帮你加深理解
- 对于串行队列:GCD给它关联的是单线程(或者说复用单线程),任务必须按顺序执行。
sync调用会把任务插入到队列的“待执行头部”,但因为当前队列正被占用,这个新任务只能无限等待;而当前执行的任务又被sync阻塞,死锁就这么产生了。 - 对于并发队列:GCD会维护一个线程池,当收到新的任务请求(哪怕是sync的),只要池子里有空闲线程,就会分配线程来执行任务,不需要等待当前任务结束,自然就不会有死锁的土壤。
内容来源于stack exchange
相关产品推荐
相关产品推荐

