1-28.【GCD】为什么在主线程 sync 到主队列一定死锁?从调度模型解释。

2 阅读2分钟

1️⃣ 场景示例

DispatchQueue.main.sync {
    print("在主线程同步调度自己")
}
  • 当前线程:主线程
  • 目标队列:主队列(串行队列)

我们分析它的调度过程。


2️⃣ 主队列的调度模型

  1. 主队列 = 串行队列

    • 队列内部只有一个线程执行 block → FIFO 顺序
    • 队列上的任务必须按顺序执行
  2. 主线程就是执行主队列任务的线程

    • 所有主队列 block 都在主线程上执行
  3. 串行 + 单线程 = 任务执行互斥


3️⃣ sync 调度机制

  • sync 的行为:

    1. 将 block 添加到目标队列末尾
    2. 阻塞调用线程,直到 block 执行完成
  • 注意:sync 会等待 block 执行完成再返回


4️⃣ 死锁发生的原理

逐步分析:

  1. 当前在 主线程上执行任务 A(或者直接在主线程执行)

  2. 调用 DispatchQueue.main.sync { 任务 B }

    • 任务 B 被 加入主队列尾部
  3. sync 阻塞当前线程(主线程)等待任务 B 完成

  4. 主线程正被阻塞,无法执行队列上的任务 B

  5. 队列中的任务 B 永远无法开始 → 任务 A 永远等待

✅ 双方互相等待 → 死锁


4.1 用调度模型图理解

主线程 (执行主队列)
 └─ sync {任务B} --> 阻塞主线程等待任务B执行
主队列 FIFO:
 [任务B] <-- 等待主线程空闲
  • 主线程被 sync 阻塞
  • 队列的 FIFO block(任务 B)无法执行
  • 永远无法完成 → 死锁

5️⃣ 异步和并发的对比

调度方式线程是否阻塞会死锁吗?
sync 到同串行队列阻塞调用线程✅ 死锁
async 到同串行队列不阻塞❌ 安全
sync 到并发队列阻塞当前线程⚠️ 可能阻塞,但不一定死锁(取决于线程池)
async 到并发队列不阻塞✅ 安全

核心区别:阻塞 + 串行队列 + 同线程执行 = 死锁


6️⃣ 结论

为什么主线程 sync 到主队列一定死锁

  1. 主队列是 串行队列
  2. 所有任务在 主线程执行
  3. sync 阻塞调用线程(主线程)等待队列任务完成
  4. 队列任务需要 主线程执行
  5. 主线程被阻塞,队列任务无法执行 → 死锁

💡 一句话总结

主线程 sync 到主队列一定死锁,因为 主队列的唯一线程(主线程)被阻塞等待自己队列里的任务完成,而这个任务又必须在主线程上执行,形成互相等待的死锁循环。