C++怎么使用barrier_C++线程同步教程【协调】

5次阅读

std::barrier 是用于线程同步的原语,使指定数量线程在某点集体等待齐后继续执行;适用于并行迭代、分块处理等需阶段性同步的场景,需正确初始化计数并确保每线程调用 arrive_and_wait()。

C++怎么使用barrier_C++线程同步教程【协调】

std::barrier 是什么,什么时候该用它

它不是互斥锁,也不是条件变量,而是专门用来让一组线程在某个点“集体暂停、等齐了再一起往下走”的同步原语。典型场景是:多个工作线程并行处理一批数据的某一轮,必须全部完成当前轮次后,才能启动下一轮——比如并行数值迭代、分块图像处理、多阶段模拟步进。

别把它当 std::mutex 用,也别指望它保护共享变量;它只管“到齐就放行”,不带所有权或临界区语义。

怎么正确初始化和等待 barrier

std::barrier 构造时必须指定参与线程数(即“等几个人”),这个数不能改,也不能动态增减。一旦所有线程调用 arrive_and_wait(),屏障就重置,可重复使用。

常见错误:

立即学习C++免费学习笔记(深入)”;

  • 传错计数:比如用 std::Thread::hardware_concurrency() 当作实际启动的线程数,但实际只起了 3 个线程,却初始化为 8——那剩下 5 个永远等不到,程序卡死
  • 漏调用:某个线程因为异常、提前 return 或逻辑分支没走到 arrive_and_wait(),整个组就挂住
  • 跨线程复用未重置的 barrier 实例:c++20 的 std::barrier 是可重用的,但前提是所有线程都已完成上一轮等待;没人帮你检查“是否真等齐了”,靠代码逻辑保证

示例(4 个线程同步三轮):

std::barrier b{4}; for (int round = 0; round < 3; ++round) {     // 各线程做本轮计算...     do_work(round);     // 全部到这里等齐     b.arrive_and_wait(); // 注意:不是 arrive() + wait() 分开调 }

barrier 和 latch、semaphore 的关键区别

std::barrier 是“可重用的、固定人数的同步点”;std::latch 是“一次性倒计时门闩”,到达零就永远打开;std::counting_semaphore 更底层,支持 acquire/release、可超量 release,但没内置“全体到齐”语义。

选错会出问题:

  • 想做多轮同步却用了 std::latch:第一轮完就失效,第二轮调 wait() 会直接返回,失去同步效果
  • std::counting_semaphore 模拟 barrier:得自己维护计数+广播逻辑,容易漏唤醒或重复唤醒,还可能因 acquire 失败抛异常
  • std::barrier 不提供异常安全的 arrive:如果某线程在 arrive_and_wait() 前抛异常,其他线程仍在等,程序 hang 住——必须确保 arrive 调用一定发生

windows / macos / GCC 版本兼容性注意点

std::barrier 是 C++20 标准特性,但落地有温差:

  • GCC 11+ 默认启用,但需编译选项 -std=c++20(仅 -std=c++17 不行)
  • Clang 12+ 支持,macOS 上需 xcode 13.3+(对应 libc++ 13.0)
  • MSVC 19.30+(VS 2022 17.0)才完整支持,早期预览版可能缺少 arrive_and_drop() 等扩展方法

如果你的项目还要兼容 GCC 10 或旧 MSVC,别硬上 std::barrier——要么降级用 std::condition_variable + 计数器手写,要么引入 boost.thread 的 boost::barrier(行为最接近)。

真正麻烦的不是语法写不对,而是跨平台 CI 里某个编译器静默忽略 barrier 或链接失败,而本地开发环境刚好跑得通。

text=ZqhQzanResources