不一定。并行队列(concurrent queue)只“允许并发”,但并不“保证并发”。
这是 GCD 里一个非常容易被“名字误导”的点。
先给一句可以直接背的结论👇
并行队列的语义是“任务之间没有顺序依赖”,是否真的并发执行,取决于线程、系统资源和调度条件。
下面按「不会并发」的典型场景逐一拆。
1️⃣ 最根本原因:没有足够的线程
只有一个可用线程时
- 单核设备
- 系统负载极高
- 线程池被限制(省电 / 后台)
DispatchQueue.global().async { workA() }
DispatchQueue.global().async { workB() }
逻辑上可并发,但实际可能:
A 执行完
↓
B 执行
👉 并行 ≠ 并发 ≠ 并行执行(parallelism)
2️⃣ 被“人为串行化”的情况(非常常见)
① 并行队列 + barrier
queue.async(flags: .barrier) {
critical()
}
-
barrier 执行时:
- 队列表现为 临时串行
- 只有它一个在跑
-
前后的任务都被挡住
② target 到串行队列
let concurrent = DispatchQueue(label: "c", attributes: .concurrent)
concurrent.setTarget(queue: someSerialQueue)
结果:
- concurrent queue 的所有任务
- 最终在
someSerialQueue上排队 - 完全不并发
👉 “并行能力”被 target queue 吃掉了。
3️⃣ 同步调用(sync)天然会限制并发
sync 本身是阻塞语义
queue.sync {
work()
}
-
调用线程被阻塞
-
如果调用发生在:
- 当前队列
- 或受限线程环境
-
实际表现为:
- 顺序执行
4️⃣ QoS / 优先级导致“看起来不并发”
DispatchQueue.global(qos: .background).async { workA() }
DispatchQueue.global(qos: .background).async { workB() }
在以下情况下:
- App 后台
- 低电量模式
- 屏幕锁定
系统可能:
- 只给 1 条 worker thread
- 甚至暂停调度
👉 不是队列不并行,是 系统不让你并发
5️⃣ 任务本身阻塞 / 占用资源
典型反例:I/O、锁、sleep
queue.async {
lock.lock()
// long work
lock.unlock()
}
- 多个任务虽然同时开始
- 但都卡在同一把锁
- 表现为串行
或者:
queue.async {
sleep(5)
}
- worker thread 被占住
- 后续任务只能等
6️⃣ Swift Concurrency 的“假并发”情况
await withTaskGroup(of: Void.self) { group in
for _ in 0..<10 {
group.addTask {
await someActor.doWork()
}
}
}
-
所有任务并发创建
-
但:
- 都进了同一个 actor
-
actor 是串行的
👉 结构并发 ≠ 实际并发
7️⃣ 正确的心智模型(很重要)
| 概念 | 含义 |
|---|---|
| 并行队列 | 允许多个任务同时被调度 |
| 并发执行 | 任务在时间上重叠 |
| 并行执行 | 多核同时跑 |
并行队列 ≠ 并发保证器
8️⃣ 一句话终极总结(请背)
并行队列只表示任务之间没有顺序约束;在缺乏线程资源、被 barrier / target 串行化、受 QoS 或系统策略限制、或任务自身阻塞的情况下,并行队列完全可能表现为串行执行。