std::latch (C++20)
latch는 문고리라는 뜻인데 일정 조건을 만족하면 문고리가 열리면서 wait 상태였던 쓰레드들이 자신들의 일을 계속 수행할 수 있다. latch는 카운터 베이스이기 때문에 세마포어와 비슷하다고 생각할 수 있지만 세마포어와 다르게 latch는 카운터가 감소하기만하는 특징을 가지고 있다. cppreference를 보면 latch는 카운트를 reset하거나 증가시키는 방법은 없다고 정의되어있다.
latch의 초기 카운트가 2라고 가정하였을 경우

쓰레드들이 일을 수행하는데 처음 latch의 count_down() 함수를 통해 카운트가 1로 감소하고 wait()함수를 통해 해당 쓰레드는 block이 된다.

이 후 다른 쓰레드가 진행되면서 count_down()을 하면 latch의 카운트는 0이 되고, 카운트가 0이 되면서 block되었던 쓰레드가 깨어나서 자신의 일을 진행하게 된다. 이어서 count_down()을 마지막으로 호출한 쓰레드가 wait()함수를 호출하여도 이 때 카운트는 0이기 때문에 block에 빠지지 않고 자신의 일을 계속 수행하게된다.
정리하자면 latch의 count_down() 함수는 latch 카운트를 1 감소시키고 wait() 함수는 카운트가 0이 아닐 때는 쓰레드를 block 상태로 만들고 0일 때는 자신의 일을 계속 수행한다. 또한, latch를 통해 block이 된 쓰레드들은 카운트가 0이 되는 순간 다시 깨어나서 자신의 일을 진행한다.
Example Code
#include <iostream>
#include <thread>
#include <chrono>
#include <latch>
#include <vector>
std::latch latch{3};
void fn()
{
std::cout << "decrease counter" << std::endl;
latch.count_down();
std::cout << "wait" << std::endl;
latch.wait();
std::cout << "rerun" << std::endl;
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 3; i++) // 쓰레드 3개 생성
{
using namespace std::literals;
std::this_thread::sleep_for(500ms);
threads.emplace_back(std::thread(fn));
}
for (auto& thread : threads)
{
thread.join();
}
return 0;
}

예상한대로 처음 2개의 thread는 wait 상태로 넘어갔다가 깨어나고 마지막 thread는 카운트가 0인 상태에서 wait를 호출했기 때문에 바로 rerun이 출력되었다. 여기서 주의할 점이 있는데 latch는 카운트의 개수를 reset할 수 없다는 것이다. 이미 latch의 카운트가 0인데 count_down()을 한 번 더 호출한다면 0이었던 카운트가 -1(음수)이 되고 그 이후부터는 block이 된 쓰레드들이 다시 깨어날 수가 없다.
Example Code
#include <iostream>
#include <thread>
#include <chrono>
#include <latch>
#include <vector>
std::latch latch{3};
void fn()
{
std::cout << "decrease counter" << std::endl;
std::cout << "wait" << std::endl;
latch.arrive_and_wait(); // count_down()과 wait() 함수를 결합한 함수
std::cout << "rerun" << std::endl;
}
int main()
{
std::vector<std::thread> threads;
for (int i = 0; i < 4; i++)
{
using namespace std::literals;
std::this_thread::sleep_for(500ms);
threads.emplace_back(std::thread(fn));
}
for (auto& thread : threads)
{
thread.join();
}
return 0;
}

그러니까 위와 같이 latch 카운트는 3이지만 4개의 쓰레드를 돌리게 된다면 마지막 하나의 쓰레드는 영원한 wait 상태에 빠지게 된다.
'C++ > Thread' 카테고리의 다른 글
[Thread]semaphore (0) | 2022.07.26 |
---|---|
[Thread] Producer-Consumer 패턴 (0) | 2022.07.26 |
[Thread] Condition Variable (0) | 2022.07.23 |
[Thread] scoped static 초기화 (0) | 2022.07.19 |
[Thread] std::call_once (0) | 2022.07.18 |