std::call_once
std::call_once란 멀티 쓰레드 환경에서 오직 한 번만 호출이 될 수 있도록 한다.
무슨 말인지 예제 코드를 통해 알아본다.
Example Code
void print()
{
std::cout << "printed" << std::endl;
}
void fn()
{
print();
}
int main()
{
std::thread t1(fn);
std::thread t2(fn);
std::thread t3(fn);
std::thread t4(fn);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
thread 4개가 printed라는 문자열을 출력하는 간단한 예제이다. 그런데 만약 이 printed라는 문구를 오직 한 번만 호출하고 싶은 경우 즉, thread가 무수히 많아도 오직 한 번만 호출되게 만들고 싶다면 std::call_once를 사용하면 된다.
Example Code : call_once 사용
std::once_flag flag;
void print()
{
std::cout << "printed" << std::endl;
}
void fn()
{
// 여러 thread가 생성되더라도 print는 한 번만 호출
std::call_once(flag, print);
}
int main()
{
std::thread t1(fn);
std::thread t2(fn);
std::thread t3(fn);
std::thread t4(fn);
t1.join();
t2.join();
t3.join();
t4.join();
return 0;
}
위와 같이 4개의 thread에서 printed를 출력하였으나 결과에는 한 번만 출력이 되었다.
이러한 동작을 mutex와 boolean 조합으로 만들수도 있으나 이를 사용하면 boolean flag를 사용하기 위해 매 번 mutex를 lock을 하고 해제하는 과정이 필요하다. std::call_once를 사용하면 되는 것을 더 비싼 비용으로 사용하는 것이다. 그러므로 전체 프로세스에서 어떠한 함수를 오직 한 번만 사용한다면 std::call_once가 효율적인 코드를 만들 수 있게 해준다.
실제 상황에서 어떻게 사용하는가?
일반적으로는 오브젝트 초기화에 사용한다.
Example Code
class Cat
{
public:
Cat()
{
std::cout << "init cat" << std::endl;
}
void speak()
{
std::cout << "meow" << std::endl;
}
};
std::once_flag flag;
std::unique_ptr<Cat> cp = nullptr;
void fn()
{
std::call_once(flag, []() {
cp = std::make_unique<Cat>();
});
}
int main()
{
std::thread t1(fn);
std::thread t2(fn);
std::thread t3(fn);
std::thread t4(fn);
t1.join();
t2.join();
t3.join();
t4.join();
cp->speak();
return 0;
}
Cat 포인터 cp를 한 번만 초기화하고 싶으면 위 코드와 같이 call_once를 사용하면 된다. 위와 같은 경우 여러 thread가 함수 fn을 호출하더라도 cp는 한 번만 초기화가 된다. 즉, call_once는 멀티 쓰레드 환경에서 오브젝트를 한 번만 초기화하는데 사용할 수 있다.
조금 더 실제적인 상황을 보자면,
class Zoo
{
private:
std::optional<Cat> mCat; // C++ 17 optional
std::once_flag catInitFlag;
}
이와 같이 클래스 내에 한 번만 정의해주기 위해서 std::once_flag도 정의해준 후 사용하면 된다.
'C++ > Thread' 카테고리의 다른 글
[Thread] Condition Variable (0) | 2022.07.23 |
---|---|
[Thread] scoped static 초기화 (0) | 2022.07.19 |
[Thread] std::shared_mutex (0) | 2022.07.17 |
[Thread] Deadlock, std::scoped_lock (0) | 2022.07.17 |
[Thread] std::unique_lock (0) | 2022.07.17 |