为什么从并发队列异步调用DispatchQueue.main.sync成功,同步调用却失败?
GCD 队列调用死锁差异原因解析
核心前提概念
sync提交任务的核心规则:将任务提交到目标队列后,会阻塞当前调用所在的线程/队列,直到目标队列完成该任务的执行才会返回,继续执行后续逻辑async提交任务的核心规则:将任务提交到目标队列后立刻返回,不会阻塞当前线程/队列的后续执行DispatchQueue.main是串行主队列:所有提交到主队列的任务必须按顺序在主线程上执行,主线程是主队列的唯一执行线程
第一种场景:background.async 调用可正常执行的原因
你首先创建了优先级为 .background 的串行队列:
let background = DispatchQueue(label: "backgroundQueue", qos: .background, attributes: [], autoreleaseFrequency: .inherit, target: nil)
执行如下代码时:
background.async { DispatchQueue.main.sync { print("Hello from background async") } }
执行流程:
- 主线程调用
background.async,将闭包任务提交到background队列后立刻返回,不会阻塞主线程,主队列可以正常处理后续提交的任务 - background队列拿到系统分配的线程执行提交的闭包
- 闭包内调用
DispatchQueue.main.sync,将打印任务提交到主队列,同时阻塞background队列的执行线程,等待打印任务完成 - 此时主线程处于空闲状态,直接执行主队列中的打印任务,执行完成后向background队列的sync调用返回
- background队列的闭包继续执行直到结束,整个流程没有出现互相等待的情况,所以可以正常运行
第二种场景:background.sync 触发死锁的原因
执行如下代码时:
background.sync { DispatchQueue.main.sync { print("Hello from background sync") } }
执行流程走到第二步就会出现循环等待:
- 主线程调用
background.sync,将闭包任务提交到background队列,直接阻塞主线程,必须等待background队列的闭包完全执行完成,主线程才会恢复,继续处理主队列的任务 - background队列拿到线程执行闭包,调用
DispatchQueue.main.sync,将打印任务提交到主队列,同时阻塞background队列的执行线程,等待主队列完成打印任务的执行 - 此时形成死锁循环:
- 主线程被
background.sync阻塞,无法执行主队列中新增的打印任务 - background队列的闭包被
DispatchQueue.main.sync阻塞,无法执行完成向主线程返回
两边都在等待对方释放资源,永远无法继续执行,因此触发死锁。
- 主线程被
内容的提问来源于stack exchange,提问作者Rozaliia Amirova
相关产品推荐
相关产品推荐

