-
C++ future(promise, async, packaged_task)C++/C++ 멀티스레드 2022. 10. 15. 13:00728x90
오늘은 C++의 future에 대해서 알아보겠습니다.
우리는 일반적으로 코드가 실행된다고 하면, 순차적으로 실행되는 것을 생각합니다.
1~10번까지의 코드가 있다면,
1번이 실행되고 2번이
2번이 실행되고 3번이 이렇게 말입니다.
그런데 만약 1번 명령문을 수행하는데에 아주 오랜 시간이 걸리면 어떻게 될까요?
2~10번코드는 1번 명령문이 완료되기를 기다리게 됩니다.
아주 오랜시간동안요.
하지만, 1번 명령문이 나머지 2~10번 코드에 영향을 미치는 코드가 아니라면?
1번이 반환하는 결과가 당장 필요하지 않다면?
그럴 때는 1번 명령을 어떤 스레드에게 맡겨놓고,
1번 명령이 완료되기 전에, 나머지 2~10번을 수행할 수 있을것입니다.
그리고, 이 때 1번 명령의 결과값을 저~기 7이나 8번즈음에서
받기 위해서 사용하는 방법이 future입니다.
future를 이용하기위해 제공되는 세 가지 방법 promise, async, packaged_task에 대해서 알아보겠습니다.
Promise
promise는 future에게 전달할 실행결과를 저장하는 것이라고 생각하면 됩니다.
코드를 먼저 보면 이해가 빠를 겁니다.
#include <iostream> #include <thread> #include <mutex> #include <Windows.h> #include <future> __int64 Calculate() { __int64 sum = 0; for (int i = 0; i < 1000000; i++) sum += i; return sum; } void PromiseWork(std::promise<__int64>&& promise) { promise.set_value(Calculate()); } void main() { //받아올 값을 저장할 promise를 생성! std::promise<__int64> promise; //promise에 저장'될' 값을 future를 통해 출력할 수 있다. std::future<__int64> future = promise.get_future(); /*지금까지는 Calculate가 실행되지 않았지만, future를 통해서 그 결과를 받아오게 될것이다.*/ //Calculate를 실행하고, promise에 그 결과값을 셋팅한다. //promise는 copy할 수 없고, move해야한다. std::thread t(PromiseWork, std::move(promise)); //다른 작업ing... //다른 스레드가 실행한 Calculate의 결과값을 여기서 받아온다. //만약 실행이 끝나지 않았으면 block하고 끝날 때 까지 대기한다. __int64 sum = future.get(); cout << sum << endl; t.join(); }
- 참고사항
- future는 get을 한 번만 호출할 수 있다.
- promise는 set_value()를 한 번만 호출할 수 있다.
Async
번역하면, 비동기라는 의미인데
실핸순서가 선형이 아니라는 뜻입니다.
#include <iostream> #include <thread> #include <mutex> #include <Windows.h> #include <future> __int64 Calculate() { cout << "start" << endl; __int64 sum = 0; for (int i = 0; i < 10000000; i++) sum += i; cout << "ends" << endl; return sum; } void main() { std::future<__int64> future = std::async(std::launch::async, Calculate); //std::future<__int64> future = std::async(std::launch::deferred, Calculate); //std::future<__int64> future = std::async(Calculate); //다른 작업ing... __int64 sum = future.get(); cout << sum << endl; }
오히려 코드는 간단합니다.
async의 policy로 넘겨주는 인자에 대해서 알아보겠습니다.
- std::launch::async : 호출하는 순간 새로운 스레드를 하나 생성해서, 함수를 실행합니다.
- std::launch::deferred : future.get()을 하는 순간, 스레드를 생성하지 않고, 함수를 실행합니다.
- default : async | deferred : C++런타임이 알아서 결정합니다.
packaged_task
#include <iostream> #include <thread> #include <mutex> #include <Windows.h> #include <future> __int64 Calculate() { cout << "start" << endl; __int64 sum = 0; for (int i = 0; i < 10000000; i++) sum += i; cout << "ends" << endl; return sum; } void TaskWorker(std::packaged_task<__int64(void)>&& task) { task(); } void main() { //return type, parameter type을 넘겨준다. std::packaged_task<__int64(void)> task(Calculate); std::future<__int64> future = task.get_future(); std::thread t(TaskWorker, std::move(task)); __int64 sum = future.get(); cout << sum << endl; t.join(); }
packaged_task는 promise를 자동으로 생성하고, 호출한 함수의 결과를 promise에 저장해준다.
언제쓰는가?
대표적으로 예외처리할 때 스레드끼리 exception을 주고받을 때 활용할 수 있다.
#include <iostream> #include <thread> #include <mutex> #include <Windows.h> #include <future> int calculate() { throw runtime_error("Exception thrown from calculate()"); } int main() { std::future<int> future = std::async(std::launch::async, calculate); //다른 작업ing... try{ int result = future.get(); cout << result << endl; } catch (exception e) { cout << e.what() << endl; } }
728x90'C++ > C++ 멀티스레드' 카테고리의 다른 글
TLS(Thread Local Storage) (0) 2022.11.08 C++ Conditional Variable(조건변수) (0) 2022.10.14 C++ 멀티스레드 동기화, 이벤트 사용법 (0) 2022.10.12 C++, Sleep을 이용한 Lock구현 (0) 2022.10.10 C++ SpinLock구현 (0) 2022.10.09 - 참고사항