#include <future>
std::future<[type]> f;
std::promise<[type]> p;
미래에 쓰레드가 원하는 데이터를 돌려주겠다는 약속이이라고 볼수 있다.
promise가 future한테 값을 보내준다고 본다.
#include <iostream>
#include <future>
#include <chrono>
using namespace std::chrono_literals;
using std::cout;
using std::endl;
int main() {
std::promise<int> pro;
std::future<int> fut = pro.get_future();
pro.set_value(10);
cout << fut.get() << endl;
}
실행 결과
10
future에서 get을 호출하면 설정된 객체가 이동되기 대문에 get을 두번 호출할 수 없다.
set_value와 get()은 한번만 사용가능
promise가 값을 주지 않으면 실행 불가
promise는 producer 역할을 수행하고, futuer는 consumer 역할을 수행한다고 보면 되기 때문에 thread로 응용을 할 수 있다.
#include <iostream>
#include <future>
#include <chrono>
using namespace std::chrono_literals;
using std::cout;
using std::endl;
int main() {
std::promise<int> pro;
std::future<int> fut = pro.get_future();
std::thread th([](std::promise<int> pro) {
std::this_thread::sleep_for(2s);
pro.set_value(10);
}, std::move(pro));
cout << fut.get() << endl;
th.join();
}
실행 결과
10
#include <iostream>
#include <future>
#include <chrono>
using namespace std::chrono_literals;
using std::cout;
using std::endl;
int main() {
std::promise<int> pro;
std::future<int> fut = pro.get_future();
std::thread th([](std::promise<int> pro) {
try {
throw std::runtime_error("err");
}
catch (...) {
std::exception_ptr e = std::current_exception();
pro.set_exception(e);
}
}, std::move(pro));
try {
cout << fut.get() << endl;
}
catch (const std::runtime_error& err) {
cout << err.what() << endl;
}
th.join();
}
실행 결과
err
set_exception
에는 예외 객체가 아닌 catch된 예외에 관한 정보를 반환하는 exception_ptr
을 전달해야 한다.
packaged_task를 이용해 위와 같은 promise -> future 패턴을 비동기적 함수의 리턴값에 간단히 적용할 수있다.
packaged_task에 전달된 함수가 리턴할 때, 반환값을 promise에 set_value를 하고 예외가 있는 경우에는 set_exception을 한다.
#include <iostream>
#include <future>
#include <chrono>
using namespace std::chrono_literals;
using std::cout;
using std::endl;
int main() {
std::packaged_task<int(int)> task([](int value) {
if (value < 0)
throw std::runtime_error("err");
return value;
});
std::future<int> fut = task.get_future();
std::thread th(std::move(task), -1);
try {
cout << fut.get() << endl;
}
catch (const std::runtime_error& err) {
cout << err.what() << endl;
}
}
실행 결과
err
std::async([launch], [함수], [인자])
std::async
에 함수를 전달한다면 기존의 promise
나 packaged_task
와 달리 스레드를 만들어서 해당 함수를 비동기적으로 실행하고 그 결과값을 future
에 전달한다.
#include <iostream>
#include <future>
#include <chrono>
using namespace std::chrono_literals;
using std::cout;
using std::endl;
int main() {
cout << "thread ID : " << std::this_thread::get_id() << endl;
std::future<int> fut = std::async(std::launch::async | std::launch::deferred, [](int value) {
cout << "thread ID : " << std::this_thread::get_id() << endl;
if (value < 0)
throw std::runtime_error("err");
return value;
}, -1);
try {
cout << fut.get() << endl;
}
catch (const std::runtime_error& err) {
cout << err.what() << endl;
}
}
실행 결과
thread ID : 51492
thread ID : 46868
err
std::launch::async
: 스레드를 바로 생성해 인자로 전달된 함수를 실행
std::launch::deferred
: 새로운 스레드를 생성하지 않고 future.get()
이 호출 되었을 때 실행.
위 두가지는 생략이 가능하고 생략할 경우 std::launch::deferred | std::launch::async
로 대체된다.
위 코드 결과값에서 새로운 스레드가 생성된 것을 확인할 수 있다.