GCD 全称:Grand Central Dispatch ,苹果多核多线程技术,底层 C 语言写的。OC 的 API 就是 C 函数;Swift 的
DispatchQueue是对这套 C 接口的面向对象封装。 文档地址:developer.apple.com/documentati...
一、GCD 到底是干嘛的(大白话)
以前写多线程用NSThread,手动创建线程、管理线程、销毁线程,很麻烦,还容易开一大堆线程把系统资源耗光。
GCD 核心思想:把任务(要干的活)放到队列里,由系统来管理线程。你只管提交任务,线程创建、复用、销毁全部交给操作系统处理,不用你手动管。
两个基础概念一定要分清:
-
任务(Block) :你要执行的代码块,比如计算、读写文件、网络解析;
-
队列(dispatch_queue_t) :存放任务的排队容器,FIFO 先进先出。
- 串行队列:一次只跑 1 个任务;做完一个再取下一个。
- 并发队列:同时可以跑多个任务,多核 CPU 会同时执行多个 block。
⚠️重点:队列≠线程!队列只是存放任务;系统从线程池拿线程出来执行队列里的任务。同一个队列的任务不一定固定跑在同一个线程。
二、队列的 4 种来源(OC 写法)
1. 主队列 dispatch_get_main_queue()
ini
dispatch_queue_t mainQ = dispatch_get_main_queue();
- 串行队列,绑定主线程(UI 线程) 。
- 所有 UI 刷新任务必须交给主队列。
- ❌禁止在主队列用
dispatch_sync,直接死锁。
2. 系统全局并发队列 dispatch_get_global_queue
ini
// 参数1:优先级;参数2保留参数永远传0
dispatch_queue_t globalQ = dispatch_get_global_queue(QOS_CLASS_UTILITY, 0);
系统自带的并发队列池,优先用这个,不要自己随便新建并发队列。 QoS 优先级(从高到低)
QOS_CLASS_USER_INITIATED用户操作触发,需要快点出结果(下拉刷新、列表加载)QOS_CLASS_UTILITY耗时持续任务,下载、同步数据QOS_CLASS_BACKGROUND后台默默干活,清理缓存、日志归档,优先级最低
3. 手动创建串行队列
arduino
// label:调试用标签,方便Xcode看栈;属性传NULL代表串行
dispatch_queue_t serialQ = dispatch_queue_create("com.my.serial", NULL);
用途:保证任务顺序、解决多线程读写冲突。 官方建议:自定义串行队列最好设置
target指向全局队列,避免大量新建队列造成线程压力。
scss
dispatch_set_target_queue(serialQ, dispatch_get_global_queue(QOS_CLASS_UTILITY,0));
4. 手动创建并发队列
ini
dispatch_queue_t concurrentQ = dispatch_queue_create("com.my.concurrent", DISPATCH_QUEUE_CONCURRENT);
⚠️官方提醒:尽量少自己创建并发队列。并发队列多了会让系统生成大量线程,耗尽 app 线程资源。优先用系统 global 全局队列。
三、提交任务两种方式:异步 async / 同步 sync
1)dispatch_async 异步(90% 业务用这个)
less
// 把任务丢进队列,函数立刻返回,不卡住当前代码;任务在别的线程执行
dispatch_async(globalQ, ^{
//子线程:做耗时计算
dispatch_async(dispatch_get_main_queue(), ^{
//切回主线程刷新UI
});
});
真实场景:下载图片、解析大量数据,子线程干活,干完切主线程刷新界面。
2)dispatch_sync 同步,阻塞当前线程,等 block 全部做完函数才返回
less
dispatch_sync(serialQ, ^{
//执行任务
});
// 代码走到这里代表block已经全部执行完
⚠️致命坑:
less// 千万不要写!死锁!当前就在主线程,同步往主队列提交任务,互相等待卡死 dispatch_sync(dispatch_get_main_queue(), ^{});
使用场景:很少,一般用于读共享资源,线程安全读取;不要在主线程使用 sync。
四、高级组件
1. dispatch_after 延时执行(OC)
不是定时器,只是 "到时间把 block 丢进队列",不会循环。
scss
// 2秒后提交到主队列执行
dispatch_time_t time = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC));
dispatch_after(time, dispatch_get_main_queue(), ^{
NSLog(@"2秒后执行");
});
2. dispatch_group_t 调度组:等待一堆异步任务全部结束
场景:同时发起 3 个下载,全部完成之后再统一回调。
scss
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
dispatch_async(globalQ, ^{
//任务1
dispatch_group_leave(group);
});
dispatch_group_enter(group);
dispatch_async(globalQ, ^{
//任务2
dispatch_group_leave(group);
});
//所有任务完成之后,通知回调到主队列
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(@"全部任务结束,刷新UI");
});
enter 和 leave 必须成对,多 leave / 少 leave 都会 bug。
3. dispatch_barrier_async 屏障(读写锁)
专门给自己创建的并发队列使用。
- 前面所有任务跑完,才执行 barrier 的 block;
- barrier 执行期间,队列其他任务全部卡住;
- barrier 结束之后,后续任务再继续并发跑。
scss
dispatch_queue_t myConcurrentQ = dispatch_queue_create("com.rw", DISPATCH_QUEUE_CONCURRENT);
//普通并发读任务
dispatch_async(myConcurrentQ, ^{ /*读*/ });
//屏障写操作:等待前面全部读完,再执行写;写的期间其他任务等待
dispatch_barrier_async(myConcurrentQ, ^{
//写数据,保证同一时间只有这一个写操作
});
注意:不能用在系统 global 全局并发队列上,barrier 会失效!只能用自己 create 出来的并发队列。
4. dispatch_apply 并行循环(Swift 叫 concurrentPerform)
多核并行跑循环,会阻塞当前线程直到全部循环结束。适合大量批量计算。
scss
//循环跑100次,多核并行
size_t count = 100;
dispatch_apply(count, globalQ, ^(size_t i) {
NSLog(@"迭代 %zu",i);
});
//走到这里代表全部循环执行完毕
五、内存与生命周期(OC 重点)
GCD 对象(队列、group、workitem)属于引用计数管理。
- MRC 环境下:
dispatch_retain()/dispatch_release(); - ARC 下:系统自动管理,不需要手动 retain/release。
六、官方文档重点警告(踩坑汇总,白话文)
- 不要任务内部阻塞线程 如果队列里面的 block 做 sleep、同步 IO 等阻塞操作,线程被卡住,系统会不断新建更多线程来完成队列剩下任务,线程数量爆炸,app 性能暴跌。
- 不要随便新建并发队列 每一个并发队列都会占用线程资源,大量
dispatch_queue_create(...,DISPATCH_QUEUE_CONCURRENT)会耗尽线程池。尽量使用dispatch_get_global_queue全局队列。
如果需要串行任务:创建串行队列,
dispatch_set_target_queue挂靠到 global 全局队列,既保证串行,又不会额外消耗线程。
- 分清
DISPATCH_TIME_NOW(开机后的相对时间)和DISPATCH_WALLTIME_NOW(系统墙上时钟时间)。手机休眠,相对时间会暂停;墙上时钟跟随系统真实时间。 - 死锁最常见来源:同一个队列内部调用
dispatch_sync。比如串行队列 A,在 A 的 block 里面调用dispatch_sync(A,^{...}),互相等待直接死锁。
七、Swift <-> OC 简单对照,方便你切换
表格
| 功能 | Objective‑C | Swift(DispatchQueue) |
|---|---|---|
| 主队列 | dispatch_get_main_queue() |
DispatchQueue.main |
| 全局并发队列 | dispatch_get_global_queue(QOS_CLASS_UTILITY,0) |
DispatchQueue.global(qos:.utility) |
| 异步提交 | dispatch_async(q,block) |
q.async { } |
| 同步提交 | dispatch_sync(q,block) |
q.sync { } |
| 延时 | dispatch_after |
asyncAfter(deadline:) |
| 调度组 | dispatch_group_t |
DispatchGroup |
| 并行循环 | dispatch_apply |
DispatchQueue.concurrentPerform |
你之前写的 Swift 瀑布流后台计算,底层就是这套 C‑API,Swift 只是一层封装。
八、业务选型快速口诀(OC 版本)
- UI 操作:丢
dispatch_get_main_queue(); - 耗时计算、文件解析:
dispatch_async提交 global 全局队列; - 需要任务按顺序执行:自定义串行队列,记得 set_target 挂靠 global 队列;
- 多个异步任务全部做完再处理结果:用
dispatch_group_t; - 多读单写:自建并发队列 +
dispatch_barrier_async做写屏障; - 大量循环计算加速:
dispatch_apply; - 永远不要在主队列执行
dispatch_sync,避免死锁。