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











