DispatchQueue 是 GCD(Grand Central Dispatch)的核心抽象,它本质上是一个 任务调度管理器,而不是线程本身。
本质特性
1. 任务队列
- 存储要执行的任务(闭包或函数)
- 遵循 FIFO(先进先出)原则
- 可以是串行(serial)或并发(concurrent)
// 串行队列 - 一次执行一个任务
let serialQueue = DispatchQueue(label: "com.example.serial")
// 并发队列 - 可同时执行多个任务
let concurrentQueue = DispatchQueue(label: "com.example.concurrent", attributes: .concurrent)
2. 线程池管理器
- 管理底层线程池(thread pool)
- 自动复用线程,避免频繁创建销毁
- 根据系统负载动态调整线程数
与线程的关系
关键区别
| DispatchQueue | 线程 (Thread) |
|---|---|
| 高级抽象,任务调度器 | 低级执行单元 |
| 管理任务执行顺序 | CPU调度的基本单位 |
| 自动线程管理 | 需要手动管理生命周期 |
| 系统优化负载均衡 | 固定资源占用 |
实际关系
// 示例:队列与线程的关系
let queue = DispatchQueue(label: "test", attributes: .concurrent)
for i in 1...5 {
queue.async {
print("任务 (i) 在线程: (Thread.current)")
}
}
// 可能输出(线程会被复用):
// 任务1在线程: <NSThread: 0x...>{number = 4, name = (null)}
// 任务2在线程: <NSThread: 0x...>{number = 5, name = (null)}
// 任务3在线程: <NSThread: 0x...>{number = 4, name = (null)} // 线程复用!
重要特性
1. 线程复用
// GCD 自动复用线程,提高性能
// 不会为每个任务都创建新线程
2. 队列层级
// 主队列 - 主线程,UI更新
DispatchQueue.main.async {
// 更新UI
}
// 全局队列 - 系统提供的并发队列
let globalQueue = DispatchQueue.global(qos: .background)
// 自定义队列
let customQueue = DispatchQueue(label: "custom")
3. 避免线程爆炸
// ❌ 错误:直接创建大量线程
for _ in 0...1000 {
Thread {
// 工作
}.start()
}
// ✅ 正确:使用队列,系统自动管理
let queue = DispatchQueue.global()
for _ in 0...1000 {
queue.async {
// 工作 - 系统会合理分配线程
}
}
最佳实践
1. 选择合适队列
// UI更新用主队列
DispatchQueue.main.async {
// 更新UI
}
// 后台任务用全局队列
DispatchQueue.global(qos: .userInitiated).async {
// 耗时任务
DispatchQueue.main.async {
// 完成后更新UI
}
}
2. 避免阻塞主队列
// ❌ 错误:在主队列执行耗时操作
DispatchQueue.main.async {
let data = processLargeData() // 阻塞UI
updateUI(data)
}
// ✅ 正确:在后台处理
DispatchQueue.global(qos: .userInitiated).async {
let data = processLargeData()
DispatchQueue.main.async {
updateUI(data)
}
}
总结
DispatchQueue 是线程的智能调度器:
- 它 管理 任务,让开发者专注于"做什么"
- 它 隐藏 线程细节,自动优化资源使用
- 它 保证 任务按照指定顺序和方式执行
GCD 通过 DispatchQueue 将开发者从繁琐的线程管理中解放出来,让并发编程变得更简单、更高效、更安全。
👉 追问:为什么说 GCD 是“基于队列而不是线程”的并发模型?
说 GCD 是"基于队列而不是线程"的并发模型,核心在于它将并发抽象的重心从"谁执行"(线程)转移到了"什么被执行"(任务)以及"如何组织执行"(队列)。
核心思想对比
传统线程模型
// 关注的是"执行者"
let thread1 = Thread { /* 任务A */ }
let thread2 = Thread { /* 任务B */ }
thread1.start()
thread2.start()
// 你需要管理:创建线程、分配任务、协调线程、销毁线程
GCD 队列模型
// 关注的是"任务组织"
let queue = DispatchQueue(label: "work", attributes: .concurrent)
queue.async { /* 任务A */ } // 我只管提交任务
queue.async { /* 任务B */ } // 系统决定如何执行
// 你只需要:定义任务、选择队列、提交任务
为什么这个区别很重要?
1. 解耦任务与执行资源
// 传统线程:任务和线程强绑定
Thread {
downloadImage() // 任务绑定到这个特定线程
}
// GCD:任务和线程解耦
queue.async {
downloadImage() // 任务提交到队列,系统分配线程
}
// 同一个任务在不同时间可能由不同线程执行
2. 从"微观管理"到"宏观调度"
| 传统线程编程 | GCD 队列编程 |
|---|---|
| 思考:需要多少线程? | 思考:任务如何组织? |
| 担心:线程创建/销毁开销 | 专注:任务依赖和顺序 |
| 操心:线程同步和通信 | 利用:队列的同步特性 |
3. 编程模型更直观
// 用队列表达执行顺序非常自然
// 串行执行:天然保证顺序
serialQueue.async { task1() }
serialQueue.async { task2() } // 一定在 task1 之后
// 并发执行:简单明了
concurrentQueue.async { task1() }
concurrentQueue.async { task2() } // 可能并行执行
// 依赖关系:清晰表达
queue.async {
let data = fetchData()
DispatchQueue.main.async {
updateUI(with: data)
}
}
实际体现
示例:对比两种模型的复杂性
// 传统线程方式实现三个任务的串行执行
class ThreadManager {
var currentThread: Thread?
func executeSequentially() {
let thread1 = Thread {
task1()
// 需要手动协调下一个任务
let thread2 = Thread {
task2()
let thread3 = Thread {
task3()
}
thread3.start()
}
thread2.start()
}
thread1.start()
}
}
// GCD 方式实现三个任务的串行执行
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async { task1() }
serialQueue.async { task2() }
serialQueue.async { task3() }
// 简洁明了,自动保证顺序
示例:避免"线程爆炸"
// 传统方式:容易创建过多线程
for i in 0..<1000 {
Thread {
processItem(i)
}.start() // 可能创建1000个线程!
}
// GCD 方式:系统智能管理
let queue = DispatchQueue.global()
for i in 0..<1000 {
queue.async {
processItem(i) // 系统复用线程池中的线程
}
}
// 可能只用 8-64 个线程(根据 CPU 核心数优化)
GCD 的哲学转变
1. 关注点分离
// 你关心的:
- 任务是什么?
- 任务间的依赖关系?
- 任务的优先级?
- 执行顺序要求?
// 系统关心的:
- 用多少线程?
- 哪个线程执行哪个任务?
- 何时创建/销毁线程?
- 如何负载均衡?
2. 更高级的抽象
// GCD 提供了高级原语
let group = DispatchGroup() // 任务组
let semaphore = DispatchSemaphore(value: 1) // 信号量
let barrier = DispatchWorkItem(flags: .barrier) // 屏障
// 这些都在队列层面操作,不直接涉及线程
3. 性能优化透明化
// 系统可以根据情况优化:
// - 线程复用
// - 负载均衡
// - CPU 亲和性
// - 能耗优化(iOS 上特别重要)
// 你只需:queue.async { work }
// 系统决定:用哪个核、哪个线程、何时执行
现实类比
传统线程模型:自己建工厂
- 你需要:招聘工人(创建线程)
- 分配任务:A 你去拧螺丝,B 你去焊接
- 协调管理:A 做完告诉 B
- 成本高:工人闲置时也要付工资
GCD 队列模型:使用云服务平台
- 你只需:提交任务需求单(任务到队列)
- 平台负责:分配工人、调度任务、优化资源
- 你只关心:任务描述和依赖关系
- 成本优:按需使用,资源复用
总结
GCD 的"基于队列"模型之所以强大,是因为:
- 抽象层次更高:从"执行单元"抽象到"任务组织"
- 资源管理更优:系统全局优化,避免局部最优
- 编程模型更安全:减少了数据竞争和死锁的机会
- 性能更可预测:避免了线程爆炸和过度订阅
- 代码更简洁:关注业务逻辑,而不是线程管理
这种设计使得并发编程从一门"黑魔法"变成了更可控、更安全的工程实践。你不再直接指挥"士兵"(线程),而是制定"作战计划"(队列和任务),让"指挥部"(GCD)去最优执行。