티스토리 뷰

이전 포스트에서 이야기한것처럼 std::async 는 스레드를 생성하지 않을 수도 있다.

https://pppgod.tistory.com/56

불러오는 중입니다...

시동 방침을 통해 비동기로 수행되도록 할 수 있다. 시동 방침이 무엇인지 알아보자.

  • std::launch::async

이 조건문을 사용하면 항상 스레드를 생성하게 된다.

  • std::launch::deferred

해당 조건을 사용하면 get 이나 wait 을 호출할 때까지 함수가 실행되지 않는다.

위의 두가지 시동 방침을 살펴보았는데, 기본 시동 방침은 과연 무엇일까? 정답은 두가지 조건을 모두 사용한다. 다시 말하면 함수는 비동기적으로 실행될 수도 동기적으로 실행될 수도 있다. 그런데 위와 같은 특성 때문에 문제가 발생할 수 있다. 다음을 살펴보자.

  • 함수가 지연 실행될 수도 있으므로, 비동기로 실행될지 예츠이 불가능하다.
  • 함수가 get 이나 wait 을 호출하는 스레드와 다른 스레드에서 실행될지 알 수 없다.
  • get 이나 wait 호출이 일어난다는 보장이 없기 때문에 함수가 실행되지 않을 수 있다.

thread_local 변수들과도 궁합이 잘 맞지 않는다. 스레드 지역 저장소(TLS)를 읽거나 쓰는 코드가 있을때, 언제 접근하는지 예측이 불가능하기 때문이다.

 심지어는 wait 기반 루프에도 영향을 미치기도 한다. wait_for wait_until 을 사용하면 std::future_status::deffered 라는 값이 반환되기 때문이다. 이 때문에 무한 루프가 발생하기도 한다.

using namespace std::literals;

void f()
{
  std::this_thread::sleep_for(1s);
}

auto fut = std::async(f);

while (fut.wait_for(100ms) !=
       std::future_status::ready)
{
  ...
}

 위 코드에서 f 가 기존 스레드와 동시에 실행되면 문제가 발생하지 않지만 지연되는 경우에는 wait_for 의 결과가 항상 std::future_status::deffered 를 반환하여 무한 루프가 발생하게 된다. 해결 방법은 다음과 같이 구현이 가능하다.

auto fut = std::async(f);

if (fut.wait_for(0s) ==
    std::future_status::deffered)
{
  ...
} else {
  while (fut.wait_for(100ms) !=
         std::future_status::ready) {
    ...
  }
  
  ...
}
  

 이제 지연이 되더라도 코드는 문제 없이 동작하게 된다. 앞서 살펴봤던 내용에 따라 기본 시동 방침은 다음의 상황에서만 적합하다는 것을 알 수 있다.

  • get 이나 wait 를 호출하는 스레드와 반드시 동시적으로 실행되어야 하는 것은 아닐때
  • thread_local 변수들을 읽소 쓰는지가 중요하지 않을때
  • futrure 객체에 대해 get 이나 wait 이 반드시 호출된다는 보장이 있거나 실행되지 않아도 괜찮은 경우
  • wait_for wait_until 을 사용하는 코드에 지연 될 수 있다는 조건이 반영된 경우

 위 조건 중에서 하나라도 부합하지 않은 경우에는 반드시 비동기로 수행되도록 변경해야 한다. 방법은 간단하다. 시동방침을 이용하는 것이다. 만약 함수를 만들어서 구현하고 싶다면 다음과 같이 작성할 수 있다.

// C++11
template<typename F, typename... Ts>
inline
std::future<typename std::result_of<F(Ts...)>::type>
reallyAsync(F&& f, Ts&&... params)
{
  return std::async(std::launch::async,
                    std::forward<F>(f),
                    std::forward<Ts>(params)...);
}

// C++14
template<typename F, typename... Ts>
inline
auto
reallyAsync(F&& f, Ts&&... params)
{
  return std::async(std::launch::async,
                    std::forward<F>(f),
                    std::forward<Ts>(params)...);
}

 

참고 서적

스콧 마이어스, 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
글 보관함