std::async와 std::future 사용 시 주의점
2024. 2. 27. 23:56ㆍC, C++
void foo() {
// 스레드 풀에서 작업 실행
auto future = std::async([]()
{
// 1000ms (1초) 대기 후 foo 출력.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout << "foo" << std::endl;
});
}
void main() {
foo();
std::cout << "bar" << std::endl;
while (true) {
std::this_thread::yield();
}
}
위와 같이 코드가 있을 때 "bar"가 출력되고 1초 뒤에 "foo"가 출력될 것 같지만
실제로 실행해보면 1초 후에 "foo" 와 "bar" 순서로 동시에 출력된다.
이는 std::async 함수를 호출할 때 옵션으로 std::launch::async를 지정했을 경우 (기본값)
반환하는 std::future 객체가 소멸자에서 해당 비동기 동작을 join하게 되는데
foo() 함수가 종료될 때 해당 future 객체의 소멸자가 호출되면서
1초를 기다린 후에 함수가 종료된다.
아래처럼 foo() 함수의 반환형을 std::future로 변경해 std::async의 반환값을 다시 반환해
main() 함수로 제어를 넘기면 원래 의도했던 것 처럼 "bar" 출력 1초 후에 "foo" 가 출력되는 모습을 볼 수 있다.
std::future<void> foo() {
// 스레드 풀에서 작업 실행
return std::async(std::launch::async, []()
{
// 1000ms (1초) 대기 후 foo 출력.
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
std::cout << "foo" << std::endl;
});
}
void main() {
auto future = foo();
std::cout << "bar" << std::endl;
while (true) {
std::this_thread::yield();
}
}
내부적으로..
std::future는 참조 카운트를 가지고 있고 카운트가 0이 되면 자체적으로 삭제 처리를 진행
- foo() 에서 반환형으로 넘기지 않으면 카운트가 foo() 함수 종료 시점에 0이 되면서 join() 호출 -> 블로킹
- foo() 함수에서 반환형으로 넘기게 되면 이동 생성자에 의해서 main() 함수까지 삭제 처리가 지연된다.