常见并发问题
问题
iOS 开发中常见的并发问题有哪些?如何预防和排查?
答案
1. 死锁
// 经典死锁:主队列 sync
DispatchQueue.main.sync { // ❌ 主线程等自己执行完 → 死锁
print("永远不执行")
}
// 串行队列嵌套 sync
let queue = DispatchQueue(label: "serial")
queue.async {
queue.sync { // ❌ 外层任务等内层完成,内层等外层让出 → 死锁
print("永远不执行")
}
}
2. 数据竞争
// ❌ 多线程同时读写
var count = 0
DispatchQueue.concurrentPerform(iterations: 1000) { _ in
count += 1 // 数据竞争!结果 < 1000
}
// ✅ 使用锁或 Actor
actor Counter {
var count = 0
func increment() { count += 1 }
}
3. 线程爆炸
// ❌ 大量 sync 调用阻塞线程 → GCD 持续创建新线程
for i in 0..<1000 {
DispatchQueue.global().async {
semaphore.wait() // 如果信号量长时间等待,线程被阻塞
// ...
semaphore.signal()
}
}
// ✅ 使用 Swift Concurrency(不阻塞线程)
await withTaskGroup(of: Void.self) { group in
for i in 0..<1000 {
group.addTask { await process(i) }
}
}
4. 优先级反转
高优先级任务等待低优先级任务释放资源,而低优先级任务因 CPU 时间不足迟迟无法完成。
os_unfair_lock替代OSSpinLock(自旋锁)- GCD 的 QoS 自动处理优先级提升
排查工具
| 工具 | 用途 |
|---|---|
| Thread Sanitizer(TSan) | 编译时检测数据竞争 |
| Instruments - Thread States | 线程状态分析 |
| Xcode Runtime Issue | 运行时并发警告 |
| Swift 6 strict concurrency | 编译器静态分析 |
Thread Sanitizer
Product → Scheme → Edit Scheme → Diagnostics → Thread Sanitizer ✅
TSan 会在运行时检测并报告数据竞争,开发和测试阶段必开。
常见面试问题
Q1: 如何排查死锁?
答案:
- Debug 时暂停运行,查看 Thread 列表
- 多个线程卡在同一个
sync或wait→ 死锁 - 使用 Instruments 的 System Trace 分析线程阻塞情况
- 遵循规则:不要在串行队列中对自身 sync
Q2: Swift 6 的 strict concurrency 解决什么问题?
答案:Swift 6 默认开启严格并发检查,编译器会在编译时发现:
- 跨并发域传递非
Sendable类型 - 非隔离上下文访问 actor 的属性
- 闭包捕获可变状态的并发风险
这让大部分并发 Bug 在编译时就被捕获,而非运行时崩溃。