thread_barrier
thread_barrier (스레드 배리어)
std::barrier는 여러 스레드가 특정 지점에 모두 도착할 때까지 기다리도록 하는 동기화 도구예요. 한 번 사용하고 버리는 std::latch와 달리 재사용이 가능하고, 모든 스레드가 도착했을 때 실행할 콜백 함수를 지정할 수 있어요. C++20부터 사용할 수 있어요.
출처: cppreference
본문
<barrier> 헤더에 정의됨 |
||
|---|---|---|
template < class CompletionFunction = /* see below */ > class barrier ; |
(C++20부터) |
std::barrier 클래스 템플릿은 알려진 크기의 스레드 그룹이 모두 배리어에 도달할 때까지 해당 그룹의 스레드들을 차단하는 스레드 조정 메커니즘을 제공해요. std::latch와 달리 배리어는 재사용 가능해요. 도착한 스레드 그룹이 차단 해제되면 배리어를 다시 사용할 수 있죠. 또한 std::latch와 달리 배리어는 스레드를 차단 해제하기 전에 비어 있을 수도 있는 호출 가능 객체를 실행해요.
배리어 객체의 수명은 하나 이상의 단계(phase)로 구성돼요. 각 단계는 대기 스레드가 차단되는 단계 동기화 지점(phase synchronization point)을 정의해요. 스레드는 arrive를 호출하여 배리어에 도착할 수 있지만, 단계 동기화 지점에서 대기를 연기할 수 있어요. 그런 스레드는 나중에 wait를 호출하여 단계 동기화 지점에서 차단될 수 있어요.
배리어 단계는 다음 단계들로 구성돼요:
arrive나arrive_and_drop을 호출할 때마다 expected count가 감소해요.- expected count가 0에 도달하면 단계 완료 단계(phase completion step)가 실행돼요. 즉,
completion이 호출되고 단계 동기화 지점에서 차단된 모든 스레드가 차단 해제돼요. 완료 단계의 끝은 완료 단계에 의해 차단 해제된 모든 호출이 반환되기 전에 강하게 발생(happens-before)해요. expected count가 0에 도달한 후 정확히 한 번, 어떤 스레드가arrive,arrive_and_drop또는wait를 호출하는 동안 완료 단계를 실행해요. 단, 어떤 스레드도wait를 호출하지 않으면 단계가 실행될지 여부는 구현 정의예요. - 완료 단계가 끝나면 expected count는 생성 시 지정된 값에서 그동안의
arrive_and_drop호출 횟수를 뺀 값으로 재설정되고 다음 배리어 단계가 시작돼요.
배리어의 멤버 함수(소멸자 제외)를 동시에 호출해도 데이터 경쟁이 발생하지 않아요.
템플릿 매개변수
| CompletionFunction | - | 함수 객체 타입 |
|---|---|---|
CompletionFunction은 MoveConstructible 및 Destructible 요구 사항을 충족해야 해요. std::is_nothrow_invocable_v<CompletionFunction&>가 true여야 해요. |
CompletionFunction의 기본 템플릿 인자는 DefaultConstructible 요구 사항도 충족하는 불특정 함수 객체 타입이에요. 그 타입의 lvalue를 인자 없이 호출해도 아무 효과가 없어요.
멤버 타입
| 이름 | 정의 |
|---|---|
arrival_token |
MoveConstructible, MoveAssignable 및 Destructible 요구 사항을 충족하는 불특정 객체 타입 |
데이터 멤버
| 멤버 | 정의 |
|---|---|
CompletionFunction completion |
모든 단계 완료 단계에서 호출되는 완료 함수 객체 (exposition-only 멤버 객체*) |
멤버 함수
| 함수 | 설명 |
|---|---|
| (constructor) | 배리어를 생성해요 (public 멤버 함수) |
| (destructor) | 배리어를 소멸해요 (public 멤버 함수) |
operator= [deleted] |
배리어는 대입할 수 없어요 (public 멤버 함수) |
arrive |
배리어에 도착하고 expected count를 감소시켜요 (public 멤버 함수) |
wait |
단계 완료 단계가 실행될 때까지 단계 동기화 지점에서 차단돼요 (public 멤버 함수) |
arrive_and_wait |
배리어에 도착하고 expected count를 1 감소시킨 다음 현재 단계가 완료될 때까지 차단돼요 (public 멤버 함수) |
arrive_and_drop |
이후 단계의 초기 expected count와 현재 단계의 expected count를 모두 1씩 감소시켜요 (public 멤버 함수) |
상수
| 상수 | 설명 |
|---|---|
max [static] |
구현이 지원하는 expected count의 최대값 (public static 멤버 함수) |
참고 사항
| Feature-test macro | 값 | 표준 | 기능 |
|---|---|---|---|
__cpp_lib_barrier |
201907L | (C++20) | std::barrier |
| 202302L | (C++20) (DR) | 단계 완료에 대한 완화된 보장 |
예제
#include <barrier>
#include <iostream>
#include <string>
#include <syncstream>
#include <thread>
#include <vector>
int main()
{
const auto workers = {"Anil", "Busara", "Carl"};
auto on_completion = []() noexcept
{
// locking not needed here
static auto phase =
"... done\n"
"Cleaning up...\n";
std::cout << phase;
phase = "... done\n";
};
std::barrier sync_point(std::ssize(workers), on_completion);
auto work = [&](std::string name)
{
std::string product = " " + name + " worked\n";
std::osyncstream(std::cout) << product; // ok, op<< call is atomic
sync_point.arrive_and_wait();
product = " " + name + " cleaned\n";
std::osyncstream(std::cout) << product;
sync_point.arrive_and_wait();
};
std::cout << "Starting...\n";
std::vector<std::jthread> threads;
threads.reserve(std::size(workers));
for (auto const& worker : workers)
threads.emplace_back(work, worker);
}
가능한 출력:
Starting...
Anil worked
Carl worked
Busara worked
... done
Cleaning up...
Busara cleaned
Carl cleaned
Anil cleaned
... done
결함 보고서
다음 동작 변경 결함 보고서는 이전에 발표된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| P2588R3 | C++20 | 기존의 단계 완료 보장이 하드웨어 가속을 방해할 수 있었음 | 완화됨 |
같이 보기
latch (C++20) |
단일 사용 스레드 배리어 (클래스) |
|---|