1️⃣ 基本作用
dispatch_barrier用于在并发队列中创建一个“屏障”,保证屏障前的任务全部完成后才执行屏障任务,屏障任务完成后才允许屏障后的任务继续执行。
- 本质:把并发队列临时变成“串行屏障”
- 保证 读-写或写-写安全,同时不阻塞其他并行队列的线程资源
2️⃣ 工作机制
假设有一个并发队列 concurrentQueue:
let concurrentQueue = DispatchQueue(label: "concurrent", attributes: .concurrent)
concurrentQueue.async { print("A") }
concurrentQueue.async { print("B") }
concurrentQueue.async(flags: .barrier) { print("Barrier") }
concurrentQueue.async { print("C") }
concurrentQueue.async { print("D") }
执行顺序:
- A、B 可以同时执行(并发)
- Barrier 等 A、B 完全完成 后执行
- C、D 等 Barrier 完成后再执行
⚠️ 注意:
- barrier 任务前后不会并发执行
- barrier 前的任务可以并发
- barrier 后的任务也可以并发(但 Barrier 本身是独占的)
3️⃣ 使用场景
3.1 并发队列上的读写安全
典型场景:多读少写的数据结构
var sharedArray = [Int]()
let queue = DispatchQueue(label: "concurrentQueue", attributes: .concurrent)
// 多个并发读
queue.async {
print(sharedArray.count)
}
queue.async {
print(sharedArray.last ?? 0)
}
// 写操作使用 barrier
queue.async(flags: .barrier) {
sharedArray.append(42)
}
解释:
- 多个读可以并发执行 → 高性能
- 写操作必须等前面读完成,并且执行时阻止其他读写 → 数据安全
Barrier 是 并发队列上的写锁机制
3.2 替代传统锁(NSLock / DispatchSemaphore)
-
可以用 barrier 替代全局锁对共享资源加锁
-
优势:
- 保持 读操作并发性
- 写操作串行化,不阻塞其他队列的线程池
-
适合 读多写少场景
3.3 串行队列上的 barrier 无效
let serialQueue = DispatchQueue(label: "serial")
serialQueue.async(flags: .barrier) { ... }
-
barrier 在串行队列上 没有任何特殊作用
-
原因:
- 串行队列天然保证同一时间只执行一个任务
- barrier 只是多余标记
4️⃣ 使用注意事项
-
只能在自定义并发队列上使用
- global queue 不允许 barrier
-
不要在主队列上使用 barrier
- 主队列是串行的,没意义
-
同步 barrier(sync + barrier)可能死锁
- 原因和普通
sync一样,当前线程可能阻塞自己
- 原因和普通
5️⃣ 总结
作用:
- 在并发队列中创建一个排它屏障
- 屏障前的任务先执行,屏障独占执行,屏障后的任务再继续并发执行
典型场景:
- 并发读 + 写操作的共享数据
- 读多写少,避免使用锁仍保证线程安全
- 替代 NSLock / semaphore 进行高性能读写控制
💡 一句话总结:
dispatch_barrier就是给并发队列加了一道“独占关卡”,保证关键写操作在队列中独占执行,而不影响前后的并发读任务。