std::latch.wait()不能反复使用,因为它是一次性同步原语,计数减至0后所有wait()调用立即返回,不支持重置;需多轮等待时应改用std::barrier。

latch.wait() 为什么不能反复用
std::latch 是一次性同步原语,构造时指定计数 n,每次调用 count_down() 减 1,减到 0 后所有阻塞在 wait() 的线程被唤醒;但一旦触发,wait() 就不再阻塞,后续调用立即返回——它不支持重置或重复等待。
所以如果你需要“等多个任务同时就绪 → 执行某操作 → 再等下一轮就绪”,std::latch 本身做不到,必须换用 std::barrier 或手动管理状态。
用 barrier 替代 latch 实现多轮就绪等待
std::barrier 是为循环协同设计的:每个参与者调用 arrive_and_wait(),当第 n 个到达时,所有线程被同时释放,并自动重置内部计数——这才是“等待多个并发任务同时就绪”的正确工具。
示例场景:启动 4 个 worker 线程做初始化,全部完成后再继续:
立即学习“C++免费学习笔记(深入)”;
std::barrier sync{4};
std::vector<std::thread> workers;
for (int i = 0; i < 4; ++i) {
workers.emplace_back([&sync] {
// 模拟初始化工作
do_init_work();
sync.arrive_and_wait(); // 全部到这里才一起往下走
});
}
for (auto& t : workers) t.join();
// 此时所有 worker 已完成初始化
- 注意
std::barrier构造参数是参与方总数,不是任务数;每个线程调用一次arrive_and_wait()即算一个“到达” - 如果某个线程提前退出(比如抛异常),没调用
arrive_and_wait(),会导致其他线程永久阻塞——务必确保每个参与者都抵达 - C++20 起支持带回调的
std::barrier<f></f>,可在每轮释放前执行一次清理逻辑
latch 真实适用场景:单次信号广播
std::latch 不是“等就绪”,而是“等完成”:适合主线程启动一批异步任务后,只关心“它们全都做完没”,不关心中间状态,也不打算再等第二轮。
常见误用:用 latch 做多阶段协调(比如“等加载完 → 渲染 → 等渲染完 → 上屏”),这会因无法重置而失败。
- 典型正确用法:主线程创建
latch l{5},启动 5 个std::async,每个结束时调用l.count_down(),主线程l.wait()等全部返回 - 若需分阶段,应为每阶段新建独立
latch,而不是复用同一个 - 性能上,
latch和barrier都是无锁实现(基于原子操作),但barrier多一次函数对象调用开销,实际差异可忽略
跨平台兼容性与替代方案
MSVC、Clang、GCC 对 C++20 std::latch 和 std::barrier 支持已较稳定,但部分旧版 STL(如 libstdc++ 12 之前)可能缺失或有 bug;若需兼容 C++17,可用 std::condition_variable + std::mutex + 计数器模拟,但易出竞态。
- 手动模拟
barrier时,务必用notify_all()而非notify_one(),否则可能漏唤醒 - Windows 上可考虑
WaitForMultipleObjects(仅限 HANDLE 任务),但不适用于 std::thread 或协程 - 更现代的选择:C++26 提案中的
std::flex_barrier支持动态调整参与者数量,但目前无主流实现
真正麻烦的从来不是选 latch 还是 barrier,而是没想清楚“我到底要等的是‘完成’还是‘就绪’,以及要不要再来一回”。


















