GCD 详解 - 从入门到精通
📌 快速参考卡片
csharp
┌─────────────────────────────────────────────────┐
│ GCD 核心概念 │
│ ├─ 队列(Queue):任务容器 │
│ │ ├─ 串行队列:一次一个任务 │
│ │ └─ 并行队列:同时多个任务 │
│ ├─ 执行方式:如何提交任务 │
│ │ ├─ sync:同步,阻塞等待 │
│ │ └─ async:异步,立即返回 │
│ └─ 系统队列:预定义的队列 │
│ ├─ main:主队列(串行) │
│ └─ global:全局队列(并行) │
└─────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────┐
│ GCD 高级特性 │
│ ├─ DispatchGroup:任务组管理 │
│ ├─ DispatchSemaphore:信号量控制 │
│ ├─ DispatchBarrier:栅栏任务 │
│ ├─ DispatchSource:事件源 │
│ └─ DispatchWorkItem:任务封装 │
└─────────────────────────────────────────────────┘
一、GCD 常用方法速查表
📋 最常用的 GCD 方法
1. 队列创建和获取
swift
// 创建串行队列
let serialQueue = DispatchQueue(label: "com.example.serial")
// 创建并行队列
let concurrentQueue = DispatchQueue(
label: "com.example.concurrent",
attributes: .concurrent
)
// 获取主队列
let mainQueue = DispatchQueue.main
// 获取全局队列
let globalQueue = DispatchQueue.global()
let highPriorityQueue = DispatchQueue.global(qos: .userInteractive)
let backgroundQueue = DispatchQueue.global(qos: .background)
2. 任务提交(最常用)
swift
// 异步执行(推荐,最常用)
queue.async {
// 任务代码
print("异步执行")
}
// 同步执行(谨慎使用)
queue.sync {
// 任务代码
print("同步执行")
}
// 异步执行,指定队列
DispatchQueue.main.async {
// UI 更新
self.label.text = "更新"
}
// 后台执行
DispatchQueue.global().async {
// 耗时操作
processData()
}
3. 延迟执行
swift
// 延迟执行(最常用)
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("2秒后执行")
}
// 延迟执行,指定时间
let delay = DispatchTime.now() + DispatchTimeInterval.seconds(3)
DispatchQueue.main.asyncAfter(deadline: delay) {
print("3秒后执行")
}
// 延迟执行,后台队列
DispatchQueue.global().asyncAfter(deadline: .now() + 1.0) {
print("1秒后执行")
}
4. 任务组(DispatchGroup)
swift
// 创建任务组
let group = DispatchGroup()
// 进入组
group.enter()
// 执行任务
queue.async {
// 任务代码
group.leave() // 离开组
}
// 等待所有任务完成
group.wait() // 阻塞等待
// 所有任务完成后的回调(最常用)
group.notify(queue: .main) {
print("所有任务完成")
}
5. 信号量(DispatchSemaphore)
swift
// 创建信号量(初始值 = 最大并发数)
let semaphore = DispatchSemaphore(value: 3)
// 等待信号量
semaphore.wait()
// 执行任务
queue.async {
// 任务代码
semaphore.signal() // 释放信号量
}
6. 栅栏任务(DispatchBarrier)
swift
// 栅栏任务(在并行队列中使用)
concurrentQueue.async(flags: .barrier) {
// 这个任务会等待前面的任务完成
// 执行时阻塞其他任务
print("栅栏任务")
}
7. 一次性执行(DispatchOnce)
swift
// Swift 中推荐使用静态属性实现单例
class MyClass {
static let shared = MyClass()
private init() {}
}
// Objective-C 中使用 dispatch_once
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// 只执行一次的代码
});
8. 任务封装(DispatchWorkItem)
swift
// 创建任务
let workItem = DispatchWorkItem {
print("执行任务")
}
// 执行任务
queue.async(execute: workItem)
// 等待任务完成
workItem.wait()
// 取消任务
workItem.cancel()
// 任务完成通知
workItem.notify(queue: .main) {
print("任务完成")
}
9. 定时器(DispatchSourceTimer)
swift
// 创建定时器
let timer = DispatchSource.makeTimerSource(queue: .global())
// 设置定时器(立即开始,每秒重复)
timer.schedule(deadline: .now(), repeating: 1.0)
// 设置事件处理
timer.setEventHandler {
print("定时器触发")
}
// 启动定时器
timer.resume()
// 取消定时器
timer.cancel()
🎯 最常用的 10 个方法(按使用频率)
| 排名 | 方法 | 使用场景 | 代码示例 |
|---|---|---|---|
| 1 | DispatchQueue.main.async |
UI 更新 | DispatchQueue.main.async { self.label.text = "更新" } |
| 2 | DispatchQueue.global().async |
后台任务 | DispatchQueue.global().async { processData() } |
| 3 | DispatchQueue.main.asyncAfter |
延迟执行 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) { } |
| 4 | DispatchGroup.notify |
等待多个任务 | group.notify(queue: .main) { } |
| 5 | DispatchQueue(label:) |
创建队列 | let queue = DispatchQueue(label: "com.example") |
| 6 | DispatchSemaphore |
控制并发 | let semaphore = DispatchSemaphore(value: 3) |
| 7 | DispatchGroup.enter/leave |
任务组管理 | group.enter() ... group.leave() |
| 8 | queue.async(flags: .barrier) |
读写锁 | queue.async(flags: .barrier) { } |
| 9 | DispatchWorkItem |
任务封装 | let workItem = DispatchWorkItem { } |
| 10 | DispatchSource.makeTimerSource |
定时器 | let timer = DispatchSource.makeTimerSource() |
💡 常用方法组合模式
模式1:后台处理 + 主线程更新(最常用)
swift
DispatchQueue.global().async {
// 后台处理
let data = processData()
DispatchQueue.main.async {
// 主线程更新 UI
self.updateUI(with: data)
}
}
模式2:多个任务完成后更新
swift
let group = DispatchGroup()
group.enter()
fetchData1 { group.leave() }
group.enter()
fetchData2 { group.leave() }
group.notify(queue: .main) {
// 所有任务完成,更新 UI
self.updateUI()
}
模式3:控制并发数量
swift
let semaphore = DispatchSemaphore(value: 3)
for url in urls {
semaphore.wait()
DispatchQueue.global().async {
download(url) {
semaphore.signal()
}
}
}
模式4:延迟执行
swift
// 延迟 2 秒执行
DispatchQueue.main.asyncAfter(deadline: .now() + 2.0) {
print("2秒后执行")
}
模式5:读写锁
swift
let queue = DispatchQueue(label: "cache", attributes: .concurrent)
// 读操作:并发执行
func read() -> Data? {
return queue.sync { cache[key] }
}
// 写操作:栅栏任务
func write(_ data: Data) {
queue.async(flags: .barrier) {
cache[key] = data
}
}
🔧 实用工具方法封装
1. 延迟执行工具方法
swift
extension DispatchQueue {
static func delay(_ seconds: Double, completion: @escaping () -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + seconds) {
completion()
}
}
}
// 使用
DispatchQueue.delay(2.0) {
print("2秒后执行")
}
2. 后台执行 + 主线程回调
swift
extension DispatchQueue {
static func background(_ work: @escaping () -> Void,
completion: @escaping () -> Void) {
DispatchQueue.global().async {
work()
DispatchQueue.main.async {
completion()
}
}
}
}
// 使用
DispatchQueue.background({
// 后台处理
processData()
}) {
// 主线程回调
self.updateUI()
}
3. 安全的主线程执行
swift
extension DispatchQueue {
static func safeMain(_ work: @escaping () -> Void) {
if Thread.isMainThread {
work()
} else {
DispatchQueue.main.async {
work()
}
}
}
}
// 使用:无论当前在哪个线程,都能安全地在主线程执行
DispatchQueue.safeMain {
self.label.text = "更新"
}
4. 批量任务执行
swift
extension DispatchQueue {
static func executeBatch<T>(
items: [T],
maxConcurrent: Int = 3,
processor: @escaping (T, @escaping () -> Void) -> Void,
completion: @escaping () -> Void
) {
let group = DispatchGroup()
let semaphore = DispatchSemaphore(value: maxConcurrent)
for item in items {
group.enter()
semaphore.wait()
DispatchQueue.global().async {
processor(item) {
semaphore.signal()
group.leave()
}
}
}
group.notify(queue: .main) {
completion()
}
}
}
// 使用
DispatchQueue.executeBatch(
items: imageURLs,
maxConcurrent: 3,
processor: { url, done in
downloadImage(url: url) {
done()
}
},
completion: {
print("所有图片下载完成")
}
)
📝 常用方法参数说明
DispatchQueue 初始化参数
swift
DispatchQueue(
label: String, // 队列标识符(必填)
qos: DispatchQoS = .default, // 服务质量优先级
attributes: DispatchQueue.Attributes = [], // 队列属性
autoreleaseFrequency: DispatchQueue.AutoreleaseFrequency = .inherit, // 自动释放频率
target: DispatchQueue? = nil // 目标队列
)
// 常用属性
.attributes = [] // 串行队列(默认)
.attributes = .concurrent // 并行队列
QoS 优先级说明
swift
.userInteractive // 用户交互(最高优先级)
.userInitiated // 用户发起
.default // 默认
.utility // 实用工具
.background // 后台(最低优先级)
.unspecified // 未指定
asyncAfter 参数说明
swift
func asyncAfter(
deadline: DispatchTime, // 执行时间
qos: DispatchQoS = .unspecified, // 优先级
flags: DispatchWorkItemFlags = [], // 标志
execute work: @escaping () -> Void // 任务
)
// 常用时间计算
.now() + 2.0 // 2秒后
.now() + .seconds(3) // 3秒后
.now() + .milliseconds(500) // 500毫秒后
.now() + .nanoseconds(1000000) // 1毫秒后
⚠️ 常见错误和注意事项
错误1:主线程同步调用
swift
// ❌ 错误:会死锁
DispatchQueue.main.sync {
print("这行代码永远不会执行")
}
// ✅ 正确
DispatchQueue.main.async {
print("这行代码会执行")
}
错误2:循环引用
swift
// ❌ 错误:循环引用
DispatchQueue.global().async {
self.processData() // 捕获了 self
}
// ✅ 正确:使用 weak self
DispatchQueue.global().async { [weak self] in
self?.processData()
}
错误3:忘记 leave
swift
// ❌ 错误:忘记 leave,group.notify 永远不会执行
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
// 忘记 group.leave()
}
// ✅ 正确:确保 enter 和 leave 配对
let group = DispatchGroup()
group.enter()
DispatchQueue.global().async {
processData()
group.leave() // 记得 leave
}
错误4:信号量忘记 signal
swift
// ❌ 错误:忘记 signal,其他任务无法执行
let semaphore = DispatchSemaphore(value: 1)
semaphore.wait()
// 忘记 semaphore.signal()
// ✅ 正确:使用 defer 确保释放
let semaphore = DispatchSemaphore(value: 1)
semaphore.wait()
defer { semaphore.signal() }
// 任务代码
二、GCD 基础概念
1. 什么是 GCD?
GCD(Grand Central Dispatch) 是 Apple 提供的多线程编程解决方案,基于 C 语言实现。
核心思想:
- 将任务添加到队列中
- 系统自动管理线程的创建和调度
- 开发者不需要直接管理线程
优势:
- ✅ 简单易用,API 简洁
- ✅ 自动管理线程生命周期
- ✅ 性能优秀,充分利用多核 CPU
- ✅ 线程安全,减少竞态条件
2. 核心概念
队列(Queue)
队列是任务的容器,按照 FIFO(先进先出)的顺序管理任务。
swift
// 队列只是任务的容器,不是线程
let queue = DispatchQueue(label: "com.example.queue")
任务(Task)
需要执行的代码块,通常是闭包。
swift
queue.async {
// 这是任务
print("执行任务")
}
线程(Thread)
实际执行任务的执行单元,由系统管理。
队列 → 任务 → 线程执行
二、队列类型详解
1. 串行队列(Serial Queue)
定义
串行队列一次只执行一个任务,任务按顺序执行。
swift
// 创建串行队列
let serialQueue = DispatchQueue(label: "com.example.serial")
特点
- ✅ 顺序执行:任务按添加顺序依次执行
- ✅ 单线程执行:同一时间只有一个任务在执行
- ✅ 线程安全:天然保证任务执行的顺序性
代码示例
swift
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async {
print("任务1开始")
sleep(2)
print("任务1结束")
}
serialQueue.async {
print("任务2开始")
sleep(1)
print("任务2结束")
}
// 输出顺序(固定):
// 任务1开始
// 任务1结束
// 任务2开始
// 任务2结束
应用场景
- UI 更新(主队列)
- 线程安全的数据访问
- 需要严格顺序执行的任务
- 日志记录
2. 并行队列(Concurrent Queue)
定义
并行队列可以同时执行多个任务,充分利用多核 CPU。
swift
// 创建并行队列
let concurrentQueue = DispatchQueue(
label: "com.example.concurrent",
attributes: .concurrent
)
特点
- ✅ 并发执行:多个任务可以同时在不同线程上执行
- ✅ 性能高:充分利用多核 CPU
- ⚠️ 完成顺序不确定:任务开始顺序确定,但完成顺序可能不同
代码示例
swift
let concurrentQueue = DispatchQueue(
label: "concurrent",
attributes: .concurrent
)
concurrentQueue.async {
print("任务1开始")
sleep(2)
print("任务1结束")
}
concurrentQueue.async {
print("任务2开始")
sleep(1)
print("任务2结束")
}
// 可能的输出(不确定):
// 任务1开始
// 任务2开始
// 任务2结束 (1秒后)
// 任务1结束 (2秒后)
应用场景
- 多图片下载
- 数据处理
- 网络请求并发
- CPU 密集型任务
3. 系统队列
主队列(Main Queue)
主队列是串行队列,所有任务在主线程执行。
swift
// 获取主队列
let mainQueue = DispatchQueue.main
// 所有 UI 操作必须在主队列执行
DispatchQueue.main.async {
self.imageView.image = image
self.tableView.reloadData()
}
特点:
- 串行队列
- 所有任务在主线程执行
- 用于 UI 更新
⚠️ 注意:
swift
// ❌ 死锁:在主线程同步调用主队列
DispatchQueue.main.sync {
print("这行代码永远不会执行")
}
全局队列(Global Queue)
全局队列是并行队列,系统提供的全局并发队列。
swift
// 获取全局队列
let globalQueue = DispatchQueue.global()
// 指定优先级
let highQueue = DispatchQueue.global(qos: .userInteractive)
let defaultQueue = DispatchQueue.global(qos: .default)
let backgroundQueue = DispatchQueue.global(qos: .background)
优先级(QoS - Quality of Service):
| QoS | 说明 | 使用场景 |
|---|---|---|
.userInteractive |
用户交互 | 动画、UI 更新 |
.userInitiated |
用户发起 | 用户操作触发的任务 |
.default |
默认 | 一般任务 |
.utility |
实用工具 | 下载、数据处理 |
.background |
后台 | 后台任务 |
.unspecified |
未指定 | 不推荐使用 |
三、执行方式详解
1. 同步执行(sync)
定义
同步执行会阻塞当前线程,等待任务完成后才继续执行。
swift
queue.sync {
// 任务代码
print("执行任务")
}
// 这行代码会等待上面的任务完成才执行
特点
- ✅ 阻塞当前线程:等待任务完成
- ✅ 可以获取返回值:任务完成后返回结果
- ⚠️ 可能死锁:在串行队列中嵌套使用要小心
代码示例
swift
let queue = DispatchQueue(label: "example")
print("开始")
queue.sync {
print("任务执行中")
sleep(1)
print("任务完成")
}
print("结束")
// 输出顺序(确定):
// 开始
// 任务执行中
// 任务完成
// 结束
应用场景
- 需要等待任务完成再继续
- 需要获取任务的返回值
- 串行化访问共享资源
2. 异步执行(async)
定义
异步执行不阻塞当前线程,立即返回,任务稍后执行。
swift
queue.async {
// 任务代码
print("执行任务")
}
// 这行代码立即执行,不等待上面的任务
特点
- ✅ 不阻塞当前线程:立即返回
- ✅ 提高响应性:UI 不会被阻塞
- ✅ 推荐使用:大多数情况下使用 async
代码示例
swift
let queue = DispatchQueue(label: "example")
print("开始")
queue.async {
print("任务执行中")
sleep(1)
print("任务完成")
}
print("结束")
// 输出顺序(可能):
// 开始
// 结束 ← 立即执行
// 任务执行中
// 任务完成
应用场景
- 大多数情况:推荐使用
- 不阻塞 UI 线程
- 后台处理任务
- 提高响应性
四、组合使用
1. 四种组合方式
| 队列类型 | 执行方式 | 代码示例 | 说明 | 死锁风险 |
|---|---|---|---|---|
| 串行队列 | sync | serialQueue.sync { } |
阻塞等待,按顺序执行 | ⚠️ 高(嵌套) |
| 串行队列 | async | serialQueue.async { } |
不阻塞,按顺序执行 | ✅ 低 |
| 并行队列 | sync | concurrentQueue.sync { } |
阻塞等待,并发执行 | ✅ 低 |
| 并行队列 | async | concurrentQueue.async { } |
不阻塞,并发执行 | ✅ 低 |
2. 实际应用示例
示例1:图片下载(并行队列 + async)
swift
let imageQueue = DispatchQueue(label: "image", attributes: .concurrent)
let imageURLs = ["url1", "url2", "url3"]
for url in imageURLs {
imageQueue.async {
// 下载图片
downloadImage(from: url)
}
}
示例2:数据访问(串行队列 + sync)
swift
class DataManager {
private let serialQueue = DispatchQueue(label: "data")
private var data: [String] = []
func addItem(_ item: String) {
serialQueue.async {
self.data.append(item)
}
}
func getItems() -> [String] {
return serialQueue.sync {
return self.data
}
}
}
五、GCD 高级特性
1. DispatchGroup(任务组)
定义
用于管理多个异步任务,等待所有任务完成。
基本使用
swift
let group = DispatchGroup()
let queue = DispatchQueue.global()
// 添加任务到组
group.enter()
queue.async {
print("任务1")
group.leave()
}
group.enter()
queue.async {
print("任务2")
group.leave()
}
// 等待所有任务完成
group.notify(queue: .main) {
print("所有任务完成")
}
实际应用:多图片下载
swift
let group = DispatchGroup()
let imageURLs = ["url1", "url2", "url3"]
for url in imageURLs {
group.enter()
DispatchQueue.global().async {
downloadImage(from: url) { image in
// 处理图片
group.leave()
}
}
}
group.notify(queue: .main) {
print("所有图片下载完成")
self.tableView.reloadData()
}
等待任务完成
swift
let group = DispatchGroup()
group.enter()
queue.async {
// 任务
group.leave()
}
// 阻塞等待,直到所有任务完成
group.wait()
print("所有任务完成")
2. DispatchSemaphore(信号量)
定义
用于控制并发数量,限制同时执行的任务数。
基本使用
swift
// 创建信号量,初始值为最大并发数
let semaphore = DispatchSemaphore(value: 3)
for i in 1...10 {
DispatchQueue.global().async {
// 等待信号量
semaphore.wait()
// 执行任务
print("任务 \(i) 开始")
sleep(1)
print("任务 \(i) 结束")
// 释放信号量
semaphore.signal()
}
}
// 最多同时执行 3 个任务
实际应用:控制网络请求并发数
swift
class NetworkManager {
private let semaphore = DispatchSemaphore(value: 5) // 最多5个并发请求
func fetchData(url: String, completion: @escaping (Data?) -> Void) {
DispatchQueue.global().async {
self.semaphore.wait()
// 网络请求
URLSession.shared.dataTask(with: URL(string: url)!) { data, _, _ in
completion(data)
self.semaphore.signal()
}.resume()
}
}
}
3. DispatchBarrier(栅栏任务)- 控制任务依赖和执行顺序
定义
DispatchBarrier(栅栏任务) 是 GCD 中用于控制任务执行顺序的重要方法。在并行队列中:
- 栅栏任务会等待前面的所有任务完成
- 然后执行栅栏任务
- 栅栏任务执行时,阻塞后面的任务
- 栅栏任务完成后,后面的任务才能执行
核心作用: 在并行队列中创建一个"分界点",确保栅栏前后的任务按顺序执行。
基本使用
swift
let concurrentQueue = DispatchQueue(
label: "concurrent",
attributes: .concurrent
)
// 栅栏前的任务:并发执行
concurrentQueue.async {
print("任务1开始")
sleep(1)
print("任务1结束")
}
concurrentQueue.async {
print("任务2开始")
sleep(1)
print("任务2结束")
}
concurrentQueue.async {
print("任务3开始")
sleep(1)
print("任务3结束")
}
// 栅栏任务:等待前面的任务完成,然后执行
concurrentQueue.async(flags: .barrier) {
print("栅栏任务:前面的任务都完成了")
print("栅栏任务执行中...")
sleep(1)
print("栅栏任务完成")
}
// 栅栏后的任务:等待栅栏任务完成后,并发执行
concurrentQueue.async {
print("任务4开始")
sleep(1)
print("任务4结束")
}
concurrentQueue.async {
print("任务5开始")
sleep(1)
print("任务5结束")
}
执行顺序:
markdown
1. 任务1、2、3 并发执行(可能同时执行)
2. 等待任务1、2、3 全部完成
3. 执行栅栏任务(独占执行)
4. 栅栏任务完成后,任务4、5 并发执行
执行流程图
并行队列:
┌─────────────────────────────────────────┐
│ 栅栏前的任务(并发执行) │
│ ┌─────┐ ┌─────┐ ┌─────┐ │
│ │任务1│ │任务2│ │任务3│ │
│ └─────┘ └─────┘ └─────┘ │
│ ↓ ↓ ↓ │
│ └─────────┴─────────┘ │
│ ↓ │
│ ┌─────────────────────┐ │
│ │ 栅栏任务(独占) │ │
│ │ (等待前面完成) │ │
│ └─────────────────────┘ │
│ ↓ │
│ 栅栏后的任务(并发执行) │
│ ┌─────┐ ┌─────┐ │
│ │任务4│ │任务5│ │
│ └─────┘ └─────┘ │
└─────────────────────────────────────────┘
关键特性
-
等待前面的任务完成
- 栅栏任务会等待所有在它之前提交的任务完成
- 无论这些任务是否已经开始执行
-
独占执行
- 栅栏任务执行时,队列中的其他任务必须等待
- 保证栅栏任务执行时不会有其他任务同时执行
-
阻塞后面的任务
- 栅栏任务执行完成后,后面的任务才能开始执行
-
只在并行队列有效
- 栅栏任务只在并行队列(
.concurrent)中有效 - 在串行队列中使用栅栏任务没有意义(串行队列本身就是顺序执行)
- 栅栏任务只在并行队列(
实际应用场景
应用1:读写锁(最常用)
swift
class DataCache {
private let concurrentQueue = DispatchQueue(
label: "cache",
attributes: .concurrent
)
private var cache: [String: Any] = [:]
// 读操作:并发执行(多个读操作可以同时进行)
func get(key: String) -> Any? {
return concurrentQueue.sync {
return cache[key]
}
}
// 写操作:使用栅栏,保证独占访问(写操作时,读操作必须等待)
func set(key: String, value: Any) {
concurrentQueue.async(flags: .barrier) {
self.cache[key] = value
}
}
// 批量写操作:使用栅栏保证原子性
func updateCache(_ updates: [String: Any]) {
concurrentQueue.async(flags: .barrier) {
for (key, value) in updates {
self.cache[key] = value
}
}
}
}
工作原理:
- 多个读操作可以并发执行(性能高)
- 写操作使用栅栏,确保写操作时没有读操作(数据安全)
- 写操作完成后,读操作才能继续
应用2:数据同步点
swift
class DataProcessor {
private let queue = DispatchQueue(label: "processor", attributes: .concurrent)
private var data: [Int] = []
// 阶段1:并发处理数据
func processData() {
for i in 1...10 {
queue.async {
// 并发处理
let result = self.processItem(i)
// 注意:这里需要线程安全地添加数据
}
}
// 栅栏任务:等待所有处理完成,然后同步数据
queue.async(flags: .barrier) {
print("所有数据处理完成,开始同步")
self.syncData()
}
// 阶段2:同步完成后,继续处理
queue.async {
print("开始后续处理")
}
}
private func processItem(_ item: Int) -> Int {
return item * 2
}
private func syncData() {
// 同步数据到数据库或服务器
}
}
应用3:批量操作的分界点
swift
class ImageDownloader {
private let queue = DispatchQueue(label: "download", attributes: .concurrent)
func downloadImages(_ urls: [String]) {
// 阶段1:并发下载
for url in urls {
queue.async {
self.downloadImage(url: url)
}
}
// 栅栏任务:所有下载完成后,更新UI
queue.async(flags: .barrier) {
DispatchQueue.main.async {
print("所有图片下载完成")
self.updateUI()
}
}
}
private func downloadImage(url: String) {
// 下载逻辑
}
private func updateUI() {
// 更新UI
}
}
应用4:确保初始化完成
swift
class ResourceManager {
private let queue = DispatchQueue(label: "resource", attributes: .concurrent)
private var isInitialized = false
func initialize() {
// 初始化任务:并发执行
queue.async {
self.loadConfig()
}
queue.async {
self.loadCache()
}
queue.async {
self.loadData()
}
// 栅栏任务:等待所有初始化完成
queue.async(flags: .barrier) {
self.isInitialized = true
print("初始化完成")
}
}
func useResource() {
queue.async(flags: .barrier) {
guard self.isInitialized else {
print("资源未初始化")
return
}
// 使用资源
}
}
}
注意事项
-
只在并行队列有效
swift// ✅ 正确:在并行队列中使用 let concurrentQueue = DispatchQueue(label: "queue", attributes: .concurrent) concurrentQueue.async(flags: .barrier) { } // ❌ 无效:在串行队列中使用栅栏没有意义 let serialQueue = DispatchQueue(label: "queue") serialQueue.async(flags: .barrier) { } // 串行队列本身就是顺序执行 -
栅栏任务会阻塞队列
- 栅栏任务执行时,队列中的其他任务必须等待
- 避免在栅栏任务中执行耗时操作
-
与 sync 的区别
swift// sync:阻塞当前线程,等待任务完成 queue.sync { } // 当前线程等待 // barrier:在并行队列中创建分界点 queue.async(flags: .barrier) { } // 不阻塞当前线程,但阻塞队列中的其他任务
完整示例:线程安全的数据结构
swift
class ThreadSafeArray<T> {
private let queue = DispatchQueue(label: "array", attributes: .concurrent)
private var array: [T] = []
// 读操作:并发执行
var count: Int {
return queue.sync { array.count }
}
func get(at index: Int) -> T? {
return queue.sync {
guard index < array.count else { return nil }
return array[index]
}
}
// 写操作:使用栅栏,保证独占访问
func append(_ element: T) {
queue.async(flags: .barrier) {
self.array.append(element)
}
}
func remove(at index: Int) {
queue.async(flags: .barrier) {
guard index < self.array.count else { return }
self.array.remove(at: index)
}
}
// 批量操作:使用栅栏保证原子性
func replaceAll(with newArray: [T]) {
queue.async(flags: .barrier) {
self.array = newArray
}
}
}
总结
DispatchBarrier 的核心作用:
- ✅ 在并行队列中创建"分界点"
- ✅ 确保栅栏前的任务全部完成
- ✅ 栅栏任务独占执行
- ✅ 栅栏后的任务等待栅栏完成
适用场景:
- 读写锁实现
- 数据同步点
- 批量操作的分界
- 初始化完成确认
- 线程安全的数据结构
4. DispatchSource(事件源)
定义
用于监听系统事件,如定时器、文件变化、信号等。
定时器示例
swift
// 创建定时器
let timer = DispatchSource.makeTimerSource(queue: .global())
timer.schedule(deadline: .now(), repeating: 1.0)
timer.setEventHandler {
print("定时器触发")
}
timer.resume()
// 取消定时器
// timer.cancel()
文件监控示例
swift
let fileURL = URL(fileURLWithPath: "/path/to/file")
let fileDescriptor = open(fileURL.path, O_EVTONLY)
let source = DispatchSource.makeFileSystemObjectSource(
fileDescriptor: fileDescriptor,
eventMask: .write,
queue: .global()
)
source.setEventHandler {
print("文件被修改")
}
source.resume()
5. DispatchWorkItem(任务封装)
定义
将任务封装成对象,可以取消、等待、通知。
基本使用
swift
// 创建任务
let workItem = DispatchWorkItem {
print("执行任务")
}
// 执行任务
DispatchQueue.global().async(execute: workItem)
// 等待任务完成
workItem.wait()
// 取消任务(如果还没开始执行)
workItem.cancel()
任务通知
swift
let workItem = DispatchWorkItem {
print("任务执行")
}
workItem.notify(queue: .main) {
print("任务完成")
}
DispatchQueue.global().async(execute: workItem)
六、实际应用场景
场景1:图片下载和显示
swift
func loadImage(url: String, completion: @escaping (UIImage?) -> Void) {
DispatchQueue.global().async {
// 在后台线程下载
guard let imageURL = URL(string: url),
let data = try? Data(contentsOf: imageURL),
let image = UIImage(data: data) else {
completion(nil)
return
}
// 回到主线程更新 UI
DispatchQueue.main.async {
completion(image)
}
}
}
// 使用
loadImage(url: "https://example.com/image.jpg") { image in
self.imageView.image = image
}
场景2:批量数据处理
swift
func processLargeData(data: [Int], completion: @escaping ([Int]) -> Void) {
let group = DispatchGroup()
let queue = DispatchQueue.global()
var results: [Int] = []
let lock = NSLock()
// 分批处理
let batchSize = 1000
for i in stride(from: 0, to: data.count, by: batchSize) {
let end = min(i + batchSize, data.count)
let batch = Array(data[i..<end])
group.enter()
queue.async {
// 处理批次
let processed = batch.map { $0 * 2 }
lock.lock()
results.append(contentsOf: processed)
lock.unlock()
group.leave()
}
}
group.notify(queue: .main) {
completion(results)
}
}
场景3:网络请求管理
swift
class APIManager {
private let semaphore = DispatchSemaphore(value: 3) // 最多3个并发
func request(url: String, completion: @escaping (Data?) -> Void) {
DispatchQueue.global().async {
self.semaphore.wait()
URLSession.shared.dataTask(with: URL(string: url)!) { data, _, _ in
completion(data)
self.semaphore.signal()
}.resume()
}
}
}
七、常见问题和陷阱
问题1:死锁
主队列同步调用
swift
// ❌ 死锁
DispatchQueue.main.sync {
print("这行代码永远不会执行")
}
串行队列嵌套同步调用
swift
// ❌ 死锁
let serialQueue = DispatchQueue(label: "serial")
serialQueue.sync {
serialQueue.sync {
print("这行代码永远不会执行")
}
}
解决方案
swift
// ✅ 使用 async
DispatchQueue.main.async {
print("这行代码会执行")
}
// ✅ 使用不同的队列
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async {
DispatchQueue.main.sync {
print("这行代码会执行")
}
}
问题2:循环引用
swift
// ❌ 循环引用
class ViewController: UIViewController {
func loadData() {
DispatchQueue.global().async {
self.processData() // 捕获了 self
}
}
}
// ✅ 使用 weak self
class ViewController: UIViewController {
func loadData() {
DispatchQueue.global().async { [weak self] in
self?.processData()
}
}
}
问题3:线程安全
swift
// ❌ 线程不安全
class Counter {
var count = 0
func increment() {
count += 1 // 多线程访问可能出错
}
}
// ✅ 使用串行队列保护
class Counter {
private let queue = DispatchQueue(label: "counter")
private var _count = 0
var count: Int {
return queue.sync { _count }
}
func increment() {
queue.async {
self._count += 1
}
}
}
八、性能优化建议
1. 合理选择队列类型
- 串行队列:需要顺序执行、线程安全
- 并行队列:独立任务、性能优化
2. 控制并发数量
swift
// 使用信号量控制并发数
let semaphore = DispatchSemaphore(value: 5)
3. 避免过度使用 sync
swift
// ❌ 过度使用 sync 会阻塞线程
for i in 1...100 {
queue.sync {
process(i)
}
}
// ✅ 使用 async 提高性能
for i in 1...100 {
queue.async {
process(i)
}
}
4. 使用合适的 QoS
swift
// 根据任务重要性设置优先级
DispatchQueue.global(qos: .userInteractive).async {
// 用户交互相关
}
DispatchQueue.global(qos: .background).async {
// 后台任务
}
九、面试常见问题
Q1: GCD 和 OperationQueue 的区别?
回答要点:
- GCD 更底层,基于 C 语言,性能更好
- OperationQueue 基于 GCD,提供更多功能(依赖、取消、优先级)
- GCD 适合简单任务,OperationQueue 适合复杂任务管理
Q2: 如何实现线程安全?
回答要点:
- 使用串行队列保护共享资源
- 使用锁机制(NSLock、NSRecursiveLock)
- 使用
DispatchBarrier实现读写锁 - 使用原子操作(atomic)
Q3: DispatchGroup 和 DispatchSemaphore 的区别?
回答要点:
- DispatchGroup:管理多个任务,等待所有任务完成
- DispatchSemaphore:控制并发数量,限制同时执行的任务数
Q4: 如何避免死锁?
回答要点:
- 避免在主线程使用
DispatchQueue.main.sync - 避免在串行队列中嵌套同步调用
- 使用
async代替sync - 使用不同的队列避免循环等待
十、总结
GCD 核心要点
- 队列类型:串行队列 vs 并行队列
- 执行方式:同步(sync)vs 异步(async)
- 系统队列:主队列(main)vs 全局队列(global)
- 高级特性:Group、Semaphore、Barrier、Source
最佳实践
- ✅ 大多数情况使用
async - ✅ UI 更新必须在主队列
- ✅ 耗时操作放在后台队列
- ✅ 使用串行队列保护共享资源
- ✅ 使用信号量控制并发数
- ✅ 注意循环引用和线程安全
面试回答模板
问题:介绍一下 GCD?
"GCD 是 Grand Central Dispatch 的缩写,是 iOS 开发中最常用的多线程方案。
核心概念:
- 队列分为串行队列和并行队列
- 执行方式分为同步和异步
- 主队列用于 UI 更新,全局队列用于后台任务
常用功能:
dispatch_async异步执行任务DispatchGroup管理多个异步任务DispatchSemaphore控制并发数量DispatchBarrier实现读写锁DispatchSource监听系统事件注意事项:
- 主队列同步调用会死锁
- 耗时操作放在后台队列,UI 更新回到主队列
- 注意线程安全和内存管理(循环引用)
我在项目中常用 GCD 处理网络请求、图片加载、数据处理等异步任务,确保主线程不被阻塞,保证 UI 流畅性。"
最后更新:2024