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

为何DispatchQueue.main.async内调用main.sync死锁,自定义串行队列sync则不会?

主线程调用不同串行队列sync的死锁差异解析

一、为什么调用DispatchQueue.main.sync会死锁?

先看死锁代码:

DispatchQueue.main.async {
    DispatchQueue.main.sync {
        print("this won't print")
    }
}

原因很直接:

  • 外层main.async把闭包加到主线程队列末尾,等主线程空闲时执行。
  • 当这个外层闭包在主线程启动后,我们调用了main.sync——sync的规则是阻塞当前线程,直到传入的闭包在目标队列执行完毕。
  • 但DispatchQueue.main是串行队列,任务必须按顺序执行。此时主线程正忙着执行外层的async闭包,sync传入的闭包只能被加到main队列的末尾,排在当前任务之后。
  • 这就形成了死锁:主线程被sync阻塞,等待末尾的闭包执行;而末尾的闭包又要等当前正在执行的外层闭包结束才能开始——两边互相卡住,谁也动不了。

二、为什么自定义串行队列的sync不会死锁?

再看无死锁的代码:

let customSerialQueue = DispatchQueue(label: "com.example.serialqueue")

DispatchQueue.main.async {
    // main thread
    customSerialQueue.sync {
        print("this will print")  // main thread
    }
}

你通过断点确认闭包在主线程执行,这是GCD的线程复用优化,但核心原因不在复用本身,而是自定义队列的执行状态:

  • 调用customSerialQueue.sync时,这个自定义串行队列当前没有任何任务在执行(刚创建的队列,之前没跑过任务)。
  • GCD对串行队列的sync调用有个关键逻辑:如果目标串行队列当前没有活跃任务,就会直接在当前调用线程(这里就是主线程)上执行传入的闭包,不会把闭包加到队列里排队。
  • 这种情况下,sync不需要阻塞等待队列的任务——闭包直接就在当前线程同步执行,执行完sync就结束,外层的async闭包也能正常完成,完全没有阻塞冲突。

两者的核心区别

  • 针对main队列的sync:目标队列当前正在执行任务(外层的async闭包),sync的闭包只能排队,当前线程阻塞等待排队任务,形成死锁。
  • 针对自定义串行队列的sync:目标队列当前无活跃任务,GCD直接在当前线程执行闭包,sync等待的就是当前线程上的闭包执行,执行完就释放,没有排队等待的冲突。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.29 17:52:55