프로그래밍/C++

C++ std::async 사용방법.

Hwan2 2020. 6. 10. 18:47
반응형

1. std::async에 대한 간단한 설명.

std::asyncstd::task 클래스 기반으로 만들어진 클래스로 Thread를 만들 때 사용됩니다.

std::asyncstd::thread와 달리 내부적으로 Thread Pool을 만들어 Thread를 관리하게 되며,

예외처리, 값 return 등.... std::thread보다 안정적이며 프로그래머가 사용하기 편리한 기능입니다.

std::async는 반환 값을 std::future로 받습니다.


2. std::async constructor

#include <iostream>
#include <future>

void for_print(char c) {
  for (int i = 0; i < 100; i++)
    printf("%c번 Thread : %d\n", c, i);
}

int main() {
  std::future<void> a = std::async(std::launch::async, for_print, 'a');
  std::future<void> b = std::async(std::launch::deferred, for_print, 'b');
  std::future<void> c = std::async(for_print, 'c');

  b.get();
  return 0;
}



std::async(std::launch::async, for_print, 'a')에서 std::launch::async는 바로 실행하는 선언입니다. 

즉, std::async가 a로 넘어가는 순간 바로 실행됩니다.


반면 std::async(std::launch::deferred, for_print, 'b')에서 std::launch::deferred는 실행하지 않고 대기합니다.

그 후 .get() or .wait()을 만나면 실행하게 됩니다.


이 외에도 std::launch부분을 생략할 수도 있습니다.




3. std::future_error

#include <iostream>
#include <future>


void for_print(char c) {
  for (int i = 0; i < 10; i++)
    printf("%c번 Thread : %d\n", c, i);
  
  //return 1;
}

int main() {
    auto c = std::async(for_print, 'c');
    std::future<int> d;

  try {
    c.get(); 
    d.get();
  }
  catch (const std::future_error& e) {
    std::cout << "Caught a future_error with code \"" << e.code()
      << "\"\nMessage: \"" << e.what() << "\"\n";
  }
  return 0;
}




실행결과



정의되지 않은 future에 대해서 .get()호출 시 예외를 처리해주는 코드입니다. .wait()을 해도 동일한 결과값을 얻어낼 수 있습니다.




4. std::future.wait_for()

#include <iostream>
#include <future>
#include <chrono>

const int number = 444444443;

// a non-optimized way of checking for prime numbers:
bool is_prime(int x) {
    for (int i = 2; i < x; ++i) if (x % i == 0return false;
    return true;
}

int main()
{
    // call function asynchronously:
    std::future<bool> fut = std::async(is_prime, number);

    // do something while waiting for function to set future:
    std::cout << "checking, please wait";
    std::chrono::milliseconds span(100);
    //std::chrono::duration<int> span (1);

    while (fut.wait_for(span) != std::future_status::ready) {  // == std::future_status::timeout
        std::cout << '.';
        std::cout.flush();
    }

    bool x = fut.get();     // retrieve return value

    std::cout << "\n" << number << " " << (x ? "is" : "is not") << " prime.\n";

    return 0;
}



실행결과


.wait_for()는 해당 Thread가 끝날때까지의 상태를 반환해줍니다.

인자 값으로는 std::chrono:: 클래스의 객체를 받습니다. 

시간을 지정해주고 해당 시간이 지남에 따라 Thread의 상태가 어떤지 알려주게 됩니다.



5. std::async를 이용한 parallelsum

#include <iostream>
#include <future>
#include <algorithm>

template <typename it>
int parallel_sum(it startit end) {
    int len = end - start;
    if (len <= 1)
        return 0;

    it newStart = start + 200;
    auto hendle = std::async(parallel_sum<it>, newStart, end);
    int sum = 0;
    for_each(start, newStart, [&](int n) { sum += n; /* std::cout << n << "\n"; */ });
    return sum + hendle.get();
}

int main()
{
    std::vector<int> v;
    v.reserve(1001);
    for (int i = 1; i < 1001; i++) v.emplace_back(i);

    std::cout << "sum : " << parallel_sum(v.begin(), v.end()) << "\n";
    
    return 0;
}




실행 결과


1 ~ 1000 까지의 합을 Thread 5개를 생성해 병렬로 계산한 코드입니다.


1 ~ 200.

201 ~ 400.

401 ~ 600.

601 ~ 800.

801 ~ 1000


이렇게 말이죠.

여기서 주목해야할 점은 hendle.get()함수를 통해 합을 정확하게 계산한 점입니다.

std::thread로 해당 코드를 구현한다면 상당히 복잡해질 것입니다.







끝.





반응형