「这是我参与11月更文挑战的第 4 天,活动详情查看:2021最后一次更文挑战」。
latch 和 barrier 对于线程同步机制来说很简单,它使一些线程能够等待直到计数器变为零。在 C++ 20 中,我们可能会得到三种变体的 latch 和barrier: std::latch, std::barrier,和std::flex_barrier 。
首先,有两个问题:
-
这三种同步线程的机制有什么不同? std::latch 只可以使用一次,但是 std::barrier 和 std::flex_barrier 可以多次使用 。另外,一个std::flex_barrier 允许你在计数器变为零的时候执行一个函数。
-
latch 和 barrier 支持哪些用例,而这些用例在 C++ 11 和 C++ 14 中不能用 future、线程或条件变量与锁组合来实现吗? latch 和 barrier 没有提供新的用例,但它们更容易使用。它们的性能也更高,因为它们经常在内部使用无锁机制。
下面,我将详细介绍这三种协调机制。
std::latch
latch 是一种倒计时的计数器。它的值在构造函数中设置。线程可以使用 thread.count_down_and_wait 方法来减少计数器,一直等到计数器为零。另外,thread.count_down 方法只对计数器减 1 而没有等待。 latch 有更深入的方法 thread.is_ready 来测试计数器是否为零,它还有 thread.wait 方法等待直到计数器变为零。您不可能增加或重置 std::latch 的计数器,因此不能重用它。
下面是提案 n4204 的一小段代码。
我将 std::latch completion_latch 在它的构造函数中设置为 NTASKS(第 2 行)。线程池执行了 NTASKS (第 4 - 8 行) 个 task 。在每个 task 的末尾(第 7 行),计数器将会减少。第 11 行是运行 DoWork 函数的线程的屏障,因此也是小工作流的屏障。这个线程必须等待,直到所有的任务都完成。
该建议使用 vector<thread>* ,并将动态分配的线程推给 vector。
workers.push_back(new thread([&] { ...
但以上这种写法会导致内存泄漏。相反,你应该将这些线程放入 std::unique_ptr 中,或者直接在 vector: workers 中创建它们。
workers.emplace_back[&]{ ...
这个观察结果适用于 std::barrier 和 std::flex_barrier 的示例。
更多细节可以参考 cppreference.com 上有关 std::latch 的代码样例。
#include <functional>
#include <iostream>
#include <latch>
#include <string>
#include <thread>
int main() {
struct job {
const std::string name;
std::string product{ "not worked" };
std::thread action{};
} jobs[] = { {"annika"}, {"buru"}, {"chuck"} };
std::latch work_done{ std::size(jobs) };
std::latch start_clean_up{ 1 };
auto work = [&](job& my_job) {
my_job.product = my_job.name + " worked";
work_done.count_down(); ///< work_done wait
start_clean_up.wait(); ///< 等待主线程执行完 start_clean_up.count_down()
my_job.product = my_job.name + " cleaned";
};
std::cout << "Work starting... ";
for (auto& job : jobs) {
job.action = std::thread{ work, std::ref(job) };
}
work_done.wait(); ///< 主线程等待所有子线程执行完 work_done.count_down()
std::cout << "done:\n";
for (auto const& job : jobs) {
std::cout << " " << job.product << '\n';
}
std::cout << "Workers cleaning up... ";
start_clean_up.count_down(); ///< start_clean_up wait
for (auto& job : jobs) {
job.action.join();
}
std::cout << "done:\n";
for (auto const& job : jobs) {
std::cout << " " << job.product << '\n';
}
}
std::barrier
未完待续
std::flex_barrier
未完待续