iOS 多线程本质理解:任务、队列与线程到底是什么关系?

2 阅读4分钟

iOS 多线程本质理解:任务、队列与线程到底是什么关系?

在 iOS 多线程学习中,很多人都会卡在同一个问题上:

任务是运行在线程上,还是运行在队列里?
队列是不是等于线程?串行队列是不是只有一个线程?

如果这些问题没有形成清晰的“脑内模型”,
那么同步 / 异步、串行 / 并发就只能靠死记结论。

本文将从 本质关系 + 运行线模型 出发,彻底讲清楚 GCD 的真实运行机制。


一、先给最终结论(非常重要)

任务(Block)不运行在队列里,也不运行在线程里

  • 任务:只是代码
  • 队列:只负责调度规则
  • 线程:真正执行代码的资源

正确关系是:


任务(Block)  
↓ 提交  
队列(Queue:串行 / 并发规则)  
↓ 调度  
线程(Thread:由系统管理)  
↓  
CPU 执行

队列不执行代码,线程才执行代码。


二、三个核心角色的本质

1️⃣ 任务(Block)

  • 一段待执行的代码
  • 本身不会“跑”
  • 不知道自己在哪个线程执行
dispatch_async(queue, ^{
    NSLog(@"我是任务");
});

👉 类比:一张工作单


2️⃣ 队列(Queue)

  • 不执行代码

  • 只负责两件事:

    • 任务是否按顺序执行
    • 任务是否可以同时执行

👉 类比:工头 / 排班规则


3️⃣ 线程(Thread)

  • 真正执行代码
  • 由系统统一管理
  • 可能被创建、复用、销毁

👉 类比:真正干活的工人


三、一个非常重要的认知升级

线程不“属于”某个队列
队列也不“拥有”某个线程

它们只是 临时协作关系


四、用“运行线”理解 GCD 执行过程

下面用「时间轴 + 线程线」的方式,直观展示任务如何被执行。


场景一:异步 + 串行队列

dispatch_async(serialQueue, taskA);
dispatch_async(serialQueue, taskB);

执行模型

时间 →
Thread-1: |--- taskA ---|--- taskB ---|

关键点

  • 串行队列:一次只允许一个任务执行
  • 系统通常复用同一条线程
  • 不能假设“串行队列 = 固定线程”

场景二:异步 + 并发队列

dispatch_async(globalQueue, taskA);
dispatch_async(globalQueue, taskB);
dispatch_async(globalQueue, taskC);

可能的执行情况

Thread-1: |--- taskA ---|
Thread-2: |--- taskB ---|
Thread-3: |--- taskC ---|

或:

Thread-1: |--- taskA ---|--- taskC ---|
Thread-2: |--- taskB ---|

关键点

  • 并发队列:允许多个任务同时出队
  • 线程数量由系统决定
  • dispatch 次数 ≠ 线程数量

场景三:同步 + 串行队列

dispatch_sync(serialQueue, taskA);
dispatch_sync(serialQueue, taskB);

执行模型

当前线程:
|--- taskA ---|--- taskB ---|

真相

  • sync:当前线程必须等待
  • 系统为了效率,直接在当前线程执行
  • 不会创建新线程

场景四:同步 + 并发队列(最容易误解)

dispatch_sync(globalQueue, taskA);

可能的执行模型

当前线程:
|--- taskA ---|

关键认知

并发 ≠ 一定多线程
sync 会压倒并发的“同时性”


五、主队列(Main Queue)单独理解

主队列的本质

  • 串行队列
  • 绑定主线程
  • UI 专用
主线程 RunLoop
└── Main Queue(串行)

主线程 + async 主队列(正确)

dispatch_async(dispatch_get_main_queue(), taskA);
主线程:
当前任务 → RunLoop → taskA

✔ 不阻塞
✔ UI 安全


主线程 + sync 主队列(死锁)

dispatch_sync(dispatch_get_main_queue(), taskA);

死锁运行线

主线程: | 等 taskA |
taskA : | 等主线程空 |

👉 互相等待,永远无法继续。


六、为什么“队列 ≠ 线程”是关键认知

❌ 错误模型

  • 一个队列对应一个线程
  • 串行队列只有一条线程

✅ 正确模型

  • 队列:规则
  • 线程:资源
  • 系统根据规则 动态分配线程

七、工程师级判断流程(实战非常好用)

看到一段 GCD 代码,按这 4 步想:

  1. sync 还是 async?

    • 我等不等?
  2. 串行还是并发?

    • 任务能不能一起执行?
  3. 是不是主队列?

    • 是否绑定主线程?
  4. 画运行线

    • 一条线还是多条线?
    • 会不会互相等待?

八、最终一句话总结

任务被提交到队列
队列只描述执行规则
线程是系统为了执行任务而临时调度的资源

当你能在脑子里画出“运行线”,
同步 / 异步、串行 / 并发就不再需要死记硬背。


九、延伸阅读建议

  • RunLoop 与主线程卡顿原理
  • GCD 死锁的形成条件
  • 串行队列作为“隐式锁”的工程实践
  • NSOperation 与 GCD 的选择