thread_call_once
thread_call_once (한 번만 호출하기)
std::call_once는 여러 스레드에서 동시에 호출되어도 주어진 Callable 객체를 정확히 한 번만 실행하도록 보장하는 함수예요. std::once_flag 객체와 함께 사용하며, 공유 자원의 초기화나 설정 작업을 스레드 안전하게 수행할 때 유용해요. 예외가 발생하면 다음 호출에서 다시 시도되므로, 실패해도 재시도가 가능한 구조로 동작해요.
출처: cppreference
본문
개요
<mutex> 헤더에 정의되어 있어요.
| Defined in header |
||
|---|---|---|
| template < class Callable , class ... Args > void call_once ( std :: once_flag & flag , Callable && f , Args && ... args ); | (since C++11) |
Callable 객체 f를 여러 스레드에서 동시에 호출해도 정확히 한 번만 실행해요.
상세 동작
std::call_once가 호출될 때flag가 이미f가 호출되었음을 나타내면,std::call_once는 즉시 반환해요. 이러한 호출을 수동적(passive) 호출이라고 해요.- 그렇지 않으면
std::call_once는INVOKE(std::forward<Callable>(f), std::forward<Args>(args)...)를 호출해요.std::thread생성자나std::async와 달리 인자를 이동하거나 복사하지 않아요. 다른 실행 스레드로 전송할 필요가 없기 때문이에요. 이러한 호출을 능동적(active) 호출이라고 해요. - 해당 호출이 예외를 던지면 예외는
std::call_once의 호출자에게 전파되고flag는 바뀌지 않아서, 이후 다른 호출이 다시 시도돼요. 이러한 호출을 예외적(exceptional) 호출이라고 해요. 해당 호출이 정상적으로 반환되면flag가 바뀌고, 같은flag에 대한 다른 모든std::call_once호출은 수동적(passive) 호출이 되는 것이 보장돼요. 이러한 호출을 반환적(returning) 호출이라고 해요.
같은 flag에 대한 모든 능동적 호출은 0개 이상의 예외적 호출 뒤에 하나의 반환적 호출이 이어지는 단일 전체 순서를 형성해요. 각 능동적 호출의 종료는 그 순서에서 다음 능동적 호출과 동기화(synchronizes-with)돼요.
반환적 호출의 반환은 같은 flag에 대한 모든 수동적 호출의 반환과 동기화돼요. 즉, std::call_once에 대한 모든 동시 호출은 추가 동기화 없이 능동적 호출이 만든 모든 부수 효과를 관찰할 수 있음을 보장해요.
매개변수
| flag | - | 정확히 하나의 함수가 실행되는 객체 |
|---|---|---|
| f | - | 호출할 Callable 객체 |
| args... | - | 함수에 전달할 인자들 |
반환값
(없음)
예외
std::call_once호출이 지정된 대로 실행되지 못하게 하는 조건이 있으면std::system_error가 발생해요.f가 던지는 모든 예외가 전파돼요.
참고 사항
동시에 std::call_once를 호출할 때 서로 다른 함수 f를 전달하면 어떤 f가 호출될지는 지정되지 않아요. 선택된 함수는 해당 std::call_once 호출이 실행된 동일한 스레드에서 실행돼요.
함수 로컬 정적 변수의 초기화는 여러 스레드에서 호출되어도 정확히 한 번만 발생하는 것이 보장되며, std::call_once를 사용한 동등한 코드보다 더 효율적일 수 있어요.
이 함수의 POSIX 등가물은 pthread_once예요.
예제
#include <iostream>
#include <mutex>
#include <thread>
std::once_flag flag1, flag2;
void simple_do_once()
{
std::call_once(flag1, [](){ std::cout << "Simple example: called once\n"; });
}
void may_throw_function(bool do_throw)
{
if (do_throw)
{
std::cout << "Throw: call_once will retry\n"; // this may appear more than once
throw std::exception();
}
std::cout << "Did not throw, call_once will not attempt again\n"; // guaranteed once
}
void do_once(bool do_throw)
{
try
{
std::call_once(flag2, may_throw_function, do_throw);
}
catch (...) {}
}
int main()
{
std::thread st1(simple_do_once);
std::thread st2(simple_do_once);
std::thread st3(simple_do_once);
std::thread st4(simple_do_once);
st1.join();
st2.join();
st3.join();
st4.join();
std::thread t1(do_once, true);
std::thread t2(do_once, true);
std::thread t3(do_once, false);
std::thread t4(do_once, true);
t1.join();
t2.join();
t3.join();
t4.join();
}
가능한 출력:
Simple example: called once
Throw: call_once will retry
Throw: call_once will retry
Throw: call_once will retry
Did not throw, call_once will not attempt again
결함 보고서
다음 동작 변경 결함 보고서는 이전에 게시된 C++ 표준에 소급 적용되었어요.
| DR | 적용 대상 | 게시된 동작 | 올바른 동작 |
|---|---|---|---|
| LWG 2080 | C++11 | f가 유효하지 않으면 std::invalid_argument가 던져지지만, f가 무효화되는 시나리오는 지정되지 않음 |
이 오류 조건을 제거함 |
| LWG 2442 | C++11 | 인자가 호출 전에 복사 및/또는 이동됨 | 복사/이동이 수행되지 않음 |
같이 보기
| once_flag (C++11) | call_once가 함수를 한 번만 호출하도록 보장하는 헬퍼 객체 (클래스) [편집] |
|---|---|
| C documentation for call_once |