std::latch 初始化必须指定非负整数计数值,不支持默认构造或重置,是一次性同步原语;多线程调用count_down()安全但不可超减;wait()阻塞等待、try_wait()非阻塞轮询;不可复制移动,捕获需用引用,生命周期须覆盖所有访问。

std::latch 初始化时必须指定计数值
std::latch 不支持默认构造,创建时必须传入一个非负整数作为初始计数值,否则编译失败。std::latch 的语义是“等待 N 个信号”,这个 N 必须在构造时确定且不可更改。
常见错误是误以为可以像 std::counting_semaphore 那样动态调整,但 std::latch 是一次性同步原语:计数归零后,所有阻塞的 wait() 立即返回,后续再调用 wait() 也直接返回,无法重置。
- 正确写法:
std::latch done(3);—— 等待 3 次count_down() - 错误写法:
std::latch done;(编译不过)或done.reset(5)(无此成员函数) - 若需重复使用,应换用
std::barrier或手动管理状态
多个线程调用 count_down() 是安全的,但不能超减
std::latch::count_down() 是无锁、原子递减操作,允许多线程并发调用,无需额外同步。但要注意:一旦计数值到达 0,继续调用 count_down() 虽不报错,但属于未定义行为(标准未规定,实际可能触发断言或静默忽略)。
典型场景是主线程启动若干 worker 线程,每个 worker 完成后调用一次 count_down();主线程在最后调用 wait() 阻塞等待全部完成。
- 安全用法:
done.count_down();在每个 worker 结束时调用一次,总调用次数 ≤ 初始值 - 危险用法:
done.count_down(2);(不支持多参数重载)、done.count_down();调用 4 次但初始化为 3 - 注意:
count_down()无返回值,无法判断是否已到底线,需靠逻辑保证调用次数
wait() 和 try_wait() 的行为差异很关键
wait() 是阻塞等待,直到计数归零;try_wait() 是非阻塞轮询,立即返回 bool 表示当前是否已达零值。两者都不修改计数值。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
在响应式或带超时逻辑中,try_wait() 更灵活,但要注意它不提供内存序保证(除非配合 std::memory_order 参数),而 wait() 内部有 acquire 语义,能确保看到之前 count_down() 所做的写操作。
- 推荐主线程阻塞等结果:
done.wait(); - 需要避免死锁或做其他事时:
while (!done.try_wait()) { do_work(); } - 不要用
try_wait()替代wait()在 tight loop 中空转,会浪费 CPU
std::latch 在 C++20 中不可复制、不可移动,生命周期必须明确
std::latch 删除了拷贝和移动构造/赋值函数,只能栈上构造或堆上 new(但后者需手动 delete)。这意味着你不能把它塞进 std::vector、不能作为函数值返回、也不能通过值传递给 lambda(捕获需用引用)。
最常见坑是在线程启动时按值捕获 —— [done]() { done.count_down(); } 编译失败,必须写成 [&done]() { done.count_down(); }。
- 正确捕获:
std::thread t([&done] { /* ... done.count_down(); */ }); - 错误捕获:
[done](试图拷贝)、std::move(done)(移动被禁用) - 若需跨作用域持有,考虑
std::shared_ptr<:latch></:latch>,但要确保所有线程结束前latch不销毁
真正容易被忽略的是:latch 对象本身的生命期必须覆盖所有对它的访问,哪怕只是 try_wait()。一旦 latch 析构,再调用任何成员函数都是未定义行为 —— 这比忘记加锁更隐蔽,因为编译器不会报错,运行时可能 crash 或静默失败。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










