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 步想:
-
sync 还是 async?
- 我等不等?
-
串行还是并发?
- 任务能不能一起执行?
-
是不是主队列?
- 是否绑定主线程?
-
画运行线
- 一条线还是多条线?
- 会不会互相等待?
八、最终一句话总结
任务被提交到队列
队列只描述执行规则
线程是系统为了执行任务而临时调度的资源
当你能在脑子里画出“运行线”,
同步 / 异步、串行 / 并发就不再需要死记硬背。
九、延伸阅读建议
- RunLoop 与主线程卡顿原理
- GCD 死锁的形成条件
- 串行队列作为“隐式锁”的工程实践
- NSOperation 与 GCD 的选择