iOS GCD Sync/Async执行逻辑疑问:为何串行队列sync任务阻塞其他队列async执行
GCD中sync方法跨队列阻塞现象的原理解析
现象本质
你观察到的“queueA的sync任务阻塞queueB异步执行”并不是真实发生的队列级阻塞,整个现象的核心成因,是发起sync调用的线程(Playground顶层执行线程,也就是主线程)的执行行为被卡住了,和自定义队列本身具备全局阻塞能力没有关系。
核心原理拆解
把GCD最容易混淆的两个基础规则理清楚,整个逻辑就完全通顺了:
sync/async是任务提交的行为差异,和队列本身的属性无关:async:把任务追加到目标队列的执行列表后立刻返回,不会阻塞发起提交的当前线程sync:把任务追加到目标队列的执行列表后,会彻底阻塞发起提交的当前线程,直到这个被提交的任务执行完成,当前线程才会恢复执行后续代码
- 队列本身只有串行/并发的执行模式差异,不存在“同步队列/异步队列”的分类:
- 无特殊属性初始化的
DispatchQueue是串行队列,按入队顺序逐个执行队列内的任务 - 带
.concurrent属性初始化的队列是并发队列,会从系统线程池取多个线程并行执行队列内的任务
把你代码的实际执行流一步步拆解,就能对应上你看到的输出结果:
- 无特殊属性初始化的
- 整个测试代码从Playground顶层开始执行,运行在和主队列绑定的主线程上
- 第一个循环从第一行代码就开始执行:每次调用
queueA.sync的时候,发起调用的主体都是主线程 - 因为sync会阻塞调用方,所以主线程每发一次sync调用,就得卡在原地等queueA上的闭包跑完(每次等待0.2秒),连续6次调用下来,主线程整整被卡了1.2秒
- 第二个循环里往queueB提交async任务的代码,必须由主线程执行才能生效——但主线程被前面的连续sync调用卡得根本没走到这部分代码,queueB压根没收到任何待执行的任务,自然不会输出任何😡
- 等6个sync调用全部执行完,主线程才会跑到第二个循环,一口气把6个async任务提交到queueB,queueB收到任务后才会开始执行输出😡
这就是你看到“先连续输出6个😁,再连续输出6个😡”的全部原因——不是queueA堵了queueB,是主线程被queueA的sync调用堵了,导致queueB的任务提交流程被延迟了。
常见认知误区澄清
- 自定义串行队列从来不会全局阻塞其他队列:不管queueA里跑了多久的任务,哪怕是跑死循环,只要这些任务是通过async提交的,整个系统的其他队列、主线程的执行流都不会受影响,最多就是queueA自己的队列按顺序执行后续任务而已。你观察到的主队列(主线程)被卡的现象,本质是你在主线程上直接发起了sync调用,不是queueA主动堵了主队列。
- sync方法的阻塞作用永远只针对发起调用的线程,不会作用在目标队列或者其他无关队列上。
你可以做个简单的验证实验:把第一个循环的sync调用从主线程挪到后台队列去执行,就能看到完全不一样的输出结果:
// 把原来的queueA.sync循环挪到后台队列执行,不再阻塞主线程 DispatchQueue.global().async { for _ in 0...5 { queueA.sync { Thread.sleep(forTimeInterval: 0.2) print("😁") } } }
这时候😁和😡会交叉输出,因为主线程不再被sync调用卡住,会顺利跑完第二个循环,把queueB的async任务全部及时提交,两个队列的任务就可以真正并行执行了。
内容的提问来源于stack exchange,提问作者krbiz
相关产品推荐
相关产品推荐

