7 - latch和barrier
C++20提供了两种线程协调机制,std::latch
(闩)和std::barrier
(屏障),允许任何数量的线程阻塞阻塞,直至期待数量的线程到达。
latch
闩latch
是 一次性使用 的线程协调点。一旦给定数量的线程到达latch
点后,所有线程都会解出阻塞并继续执行,就是个一次性计数器。
latch
由std::latch
实现,在<latch>
中定义,它的常用方法如下:
- 构造函数:接收到达
latch
点的所需线程数。 arrive_and_wait()
:递减latch
的计数器并阻塞,直至被唤醒。wait()
:不递减计数器并阻塞,直至被唤醒。try_wait()
:检查计时器是否到达零。count_down()
:减少计时器,不阻塞。
例如要先加载数据到内存,然后让10个线程并行处理它,可以这样做:
latch startLatch(1);
vector<jthread> threads;
for (int i = 0; i < 10; ++i)
{
threads.push_back(jthread([&startLatch]
{
// 线程初始化...
// 等待, 直到latch计数器为0
startLatch.wait();
// 处理数据
}));
}
// 读数据到内存...
// 数据读好后, 减少latch计数器,唤醒所有线程
startLatch.count_down();
在上述代码中,先初始化10个线程并让它们等待,然后读数据到内存中,读好后让latch
计数器归零,唤醒所有调用latch.wait()
的线程去处理数据。
barrier
屏障barrier
是 可重用的 线程协调机制。当给定数量的线程到达barrier
后,会执行完成阶段的回调,解除所有阻塞线程,并重置计数器,开始下一个阶段。在每个阶段中,可调节下阶段的预期线程数。
barrier
由std::barrier
实现,在barrier
中定义。它常用的方法就是arrive_and_wait()
。其余可用方法详见标准库指南。
例如如下代码,它启动4个线程,在循环中连续执行某些操作。在每次迭代中,所有线程都是用barrier
进行同步:
void onComplete() noexcept { /* ... */ }
int main()
{
const size_t thread_num = 4;
barrier barrierPoint(thread_num, onComplete);
vector<jthread> threads;
for (int i = 0; i < thread_num; ++i)
{
threads.push_back(jthread([&barrierPoint](stop_token token)
{
while (!token.stop_requested())
{
// 做计算
// 阻塞当前线程
barrierPoint.arrive_and_wait();
}
}));
}
}
参考资料
- C++20高级编程
- 同步操作 | 现代C++并发编程教程 (mq-b.github.io)