1️⃣ 目标:读多写少的安全策略
假设我们有一个 共享资源,需要:
- 多个读可以同时执行 → 并发,提高性能
- 写操作必须独占 → 保证数据一致性
dispatch_barrier 就是实现这个模式的工具。
2️⃣ 队列模型回顾
-
串行队列:一次只允许一个任务执行
-
并发队列:允许多个任务同时执行
-
自定义并发队列:
- GCD 内部维护了 任务队列 + 当前执行计数(running count)
- target queue 是 global queue 或线程池
3️⃣ barrier 的底层原理
核心概念
dispatch_barrier 本质是 队列层的屏障控制:
-
队列维护一个 当前正在执行任务的计数器(
running或executingCount) -
当 barrier 任务到达队列:
-
队列检查当前 running count:
- 如果 count > 0 → barrier 等待所有前置任务完成
- 如果 count == 0 → barrier 可以立即执行
-
-
barrier 执行期间:
- 队列 暂停派发后续任务
- 任何新的任务也不能执行,直到 barrier 完成
-
barrier 完成:
- 队列恢复派发剩余任务
- 剩余任务可以并发执行(如果是并发队列)
队列状态机示意
并发队列:
队列头: [read1, read2, barrier, read3, read4]
执行计数 running = 0
1️⃣ read1 执行 -> running = 1
2️⃣ read2 执行 -> running = 2
3️⃣ barrier 到队列头 -> 等待 running = 0
4️⃣ read1, read2 完成 -> running = 0
5️⃣ barrier 执行 -> running = 1 (独占)
6️⃣ barrier 完成 -> running = 0
7️⃣ read3, read4 开始并发执行 -> running = 2
- Barrier 期间 running = 1 → 独占
- Barrier 前/后 → 并发读 allowed
4️⃣ 关键技术点
- FIFO 队列保证顺序
- Barrier 永远在队列头执行前,队列保证前面的任务完成
- 这样读操作不会和 barrier 写操作冲突
- 执行计数 + 派发控制
- running count + 队列内部状态机
- barrier 期间暂停队列派发新任务
- barrier 完成后恢复并发执行
- 不依赖锁或互斥量
- 这是 GCD 高性能的原因
- barrier 依赖队列内部状态 + 信号通知
5️⃣ 实际使用示例
let queue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
var sharedArray = [Int]()
// 并发读
queue.async { print(sharedArray.count) }
queue.async { print(sharedArray.first ?? 0) }
// barrier 写
queue.async(flags: .barrier) {
sharedArray.append(42)
}
// barrier 后的读
queue.async { print(sharedArray.last ?? 0) }
- barrier 确保写操作独占
- barrier 前后读操作可以并发执行
- 无需手动加锁
6️⃣ 小结
Barrier 的底层实现机制:
-
队列内部维护 执行计数 + 状态机
-
Barrier 提交时:
- 等待前置任务完成
- 执行期间独占队列(暂停后续任务)
-
Barrier 完成后:
- 恢复队列派发
- 后续任务并发执行
本质上,
dispatch_barrier是 自定义并发队列上的“FIFO + 计数屏障”机制,保证了读多写少的互斥安全,而不依赖显式锁。