티스토리 뷰

 항목 37에서 합류 가능한 스레드가 동작중에 스레드 객체가 소멸되면 프로그램이 종료된다고 하였다. 스레드와 비슷한 async 객체가 남기는 future 객체는 암묵적 join암묵적 detach와 비슷한 행동을 수행한다. 그럼에도 스레드와 다르게 프로그램이 종료된다거나 미정의 행동을 하지는 않는다. 어떻게 동작할 수 있을까?

 공유 상태

 우선 스레드의 결과가 어디에 저장되는지 살펴보아야 한다. 생각할 수 있는 가능성은 두가지일 것이다. 스레드를 생성한 쪽이거나 스레드로 생각할 수 있다. 그러나 두가지 모두가 아닌 외부에 저장된다. 그 이유를 생각해보자.

  • 스레드를 생성한 쪽에 저장되는 경우

 위와 같은 경우에는 future 객체에 저장될 것이다. 그런데 future 객체는 std::shared_future 를 생성할 수 있어 future 객체가 파괴되어도 여러 번 복사할 수 있다. 그런데 만일 스레드의 결과가 복사를 지원하지 않는 경우, 다수의 미래 객체 중 어떤 것에 결과를 저장해야할지 알 수 없다.

  • 스레드에서 저장되는 경우

 스레드에 저장되는 경우에는 std::promise 에 저장될 것이다. 그런데 get 함수를 통해 결과를 얻기 전에 스레드가 종료된다고 가정하면 스레드는 종료되며 std::promise 객체가 함께 소멸될 것이다.

 위와 같은 이유로 스레드의 결과는 공유 상태(shared state)라는 곳에 저장된다. 공유 상태를 알아야하는 이유는 공유 상태가 future 객체 소멸자의 행동을 결정하기 때문이다. 그 규칙은 다음과 같다.

  • std:: async를 통해서 시동된 비지연 과제에 대한 공유 상태를 참조하는 마지막 미래 객체의 소멸자는 과제가 완료될 때까지 차단된다.
  • 다른 모든 미래 객체의 소멸자는 그냥 해당 미래 객체를 파괴한다.

 위 규칙을 간단하게 정리하면 다음과 같다.

공유 상태는 마지막 future 객체가 소멸될 때 소멸되고, 동시에 암묵적인 join을 수행한다.

문제점

 암묵적인 join을 수행하기 때문에 큰 문제가 발생한다.

 마지막 future 객체가 소멸될 때, 암묵적인 join을 수행하며 async 함수가 종료될 때까지 기다리는 것이다. 예를 들면 async 로 부터 생성된 스레드가 무한 루프를 돌고 있다면, async 를 호출한 스레드도 함께 멈추기 때문이다. 문제는 future 객체를 생성하는 방법이 async 뿐이 아니라는 것이다. 그 중 하나가 std::packaged_task 이다. std::packaged_task 는 다행히 async 에서 사용할 필요가 없다. 어차피 async 에서 future 객체를 생성하기 때문이다. 따라서 스레드에서 사용하는 것이 일반적이다. 

 std::packaged_task 가 스레드에서 사용된다면 우리는 암묵적인 join에 대해서 고려할 필요가 없어진다. 우리가 스레드를 종료할 때 3가지의 선택지로 종료한다. 각각의 선택지에 대해 future 객체의 소멸자가 수행할 행동은 다음과 같다.

  • join을 수행하는 경우

우리가 스레드에 join을 한다면 future 객체는 소멸될 때, 아무런 행동을 할 필요가 없다.

  • detach를 수행하는 경우

우리가 스레드에 detach를 한다면 future 객체는 소멸될 때, 아무런 행동을 할 필요가 없다.

  • 아무것도 하지 않는 경우

스레드가 소멸될 때, 프로그램은 종료된다.

따라서 우리는 future 객체의 소멸자에서 일어나는 일에 대해 고려할 필요가 없다.

 

참고 서적

스콧 마이어스, Effective Modern C++
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
글 보관함