1-14.【GCD】为什么 barrier 只对自定义并发队列有效?

5 阅读4分钟

1️⃣ 核心结论

Barrier 只对自定义并发队列有效,是因为 barrier 的实现依赖队列内部的任务调度控制,而全局队列和串行队列的调度机制不允许修改或者插入“屏障”。

换句话说:

  • 串行队列天然保证同一时间只执行一个任务 → barrier 多余
  • 全局队列是共享的并发队列 → barrier 无法保证独占执行

2️⃣ 串行队列为什么不需要 barrier?

串行队列特点:

  • 同一时间只允许一个任务执行
  • 队列内部天然 FIFO
串行队列:
task1 -> task2 -> task3
  • 即使没有 barrier:

    • task1 完成后才会执行 task2
    • task2 完成后才执行 task3

✅ barrier 对串行队列完全无意义


3️⃣ 全局并发队列为什么不支持 barrier?

全局队列特点:

  • 系统管理的共享队列
  • 线程池是全局共享的
  • 任务来自不同来源(App 内的多个 dispatch queue)

如果你在 global queue 上使用 barrier:

  1. 无法保证“前面任务全部完成”

    • 因为 global queue 的任务是多来源的
    • 你无法控制前面到底有哪些任务正在执行
  2. barrier 执行时无法独占线程池

    • global queue 线程可能正在执行其他队列的任务
    • 无法阻塞其他人的线程而只执行 barrier

本质原因:Barrier 需要“屏障前的任务可控,屏障期间独占线程,屏障后继续并发” ,这是自定义并发队列才能做到的。


4️⃣ 自定义并发队列如何支持 barrier

自定义并发队列特点:

  • 队列任务 只来自一个队列(你的 queue)
  • GCD 可以在内部维护一个 isExecuting 或正在执行计数器

实现机制:

  1. 提交 barrier 任务

    • 队列先等待当前队列内部的所有任务完成
    • 在 barrier 任务执行期间,不派发新的任务
  2. 执行 barrier

    • 独占执行(可能在现有线程或新线程上)
  3. Barrier 完成

    • 恢复派发后续任务
    • 后续任务继续并发执行

⚠️ 关键:这个控制仅在 自定义并发队列 内部可控

  • 全局队列:任务来自不同队列,无法保证屏障前任务全部完成
  • 串行队列:本身就是单任务串行,屏障多余

5️⃣ 总结

队列类型是否支持 barrier原因
串行队列天然串行,屏障多余
全局并发队列任务来源多,无法独占线程或保证前置任务完成
自定义并发队列队列可控,GCD 内部可以实现 barrier 屏障机制

一句话总结:Barrier 的语义是“队列内部独占执行”,而只有自定义并发队列,任务来源可控、线程可调度,才能实现这个语义。


👉 追问:在 global queue 上用 barrier 会发生什么?

global queue(全局并发队列) 上使用 dispatch_barrier,结果和你想象的屏障语义 完全不一样

一句话核心结论:

在 global queue 上使用 barrier 并不会创建独占屏障,任务只是像普通 async 一样提交执行,不保证前面任务完成或屏障期间独占线程。

下面详细拆开:


1️⃣ global queue 的特点

  • 系统管理的共享并发队列
  • 线程池是全局共享的
  • 任务可能来自多个不同队列或系统组件
  • 你无法控制“前面到底有哪些任务正在执行”

2️⃣ barrier 的语义

在自定义并发队列:

  • 屏障任务 必须等待前面队列内部任务完成
  • 屏障期间 独占执行
  • 屏障任务完成后,后续任务继续并发执行

在 global queue 上:

  • 你提交的 barrier 任务只是普通 async 任务
  • barrier 标记被忽略,系统不会等待前面的 global queue 任务
  • barrier 任务可能 与其他 global queue 任务同时执行

3️⃣ 举例

let global = DispatchQueue.global(qos: .default)

global.async { print("A") }
global.async { print("B") }
global.async(flags: .barrier) { print("Barrier") }
global.async { print("C") }

执行可能结果:

A B Barrier C
A Barrier B C
Barrier A B C
  • barrier 不会保证独占
  • 前后顺序不确定
  • 与普通 async 完全一样

4️⃣ 为什么会这样

  • barrier 的实现依赖 可控的队列任务来源 + 内部调度状态

  • global queue 任务来源多、不可控

  • GCD 无法保证:

    • 前面任务完成
    • 屏障任务独占线程

⚠️ 所以官方文档明确说明:

“Barrier only works on custom concurrent queues. Applying a barrier on a global queue has no effect.”


5️⃣ 总结

  • global queue + barrier = barrier 标记被忽略
  • 任务行为等同普通 async
  • 不要在全局队列上依赖 barrier 实现读写屏障

💡 实践建议:

  • 共享资源的并发读 + 写屏障 → 使用自定义并发队列 + barrier
  • 全局队列只适合普通并发任务,不用于屏障控制