future & promise 사용법
- promise 객체를 통해 자신과 연결된 future 객체를 생성할 수 있습니다. promise 객체에 값이 셋팅되었을 때 future를 통해서 해당 값을 가져올 수 있습니다.
future & promise 사용법 예시
#include <iostream>
#include <future>
using namespace std;
void WorkerThread(promise<string>* p, const string *name)
{
try
{
// name이 nullptr일 경우 throw
if (name == nullptr)
{
throw runtime_error("name is nullptr");
}
// promise에 값을 셋팅
p->set_value(*name + ", Hello\n");
}
catch (const std::exception& exp)
{
// promise에 exception_ptr을 셋팅
p->set_exception(make_exception_ptr(exp));
}
return;
}
int main()
{
promise<string> p;
// 자신과 연결된 promise 객체를 생성
future<string> f = p.get_future();
string str = "devhun";
thread t(WorkerThread, &p, nullptr);
f.wait();
try
{
// promise에 셋팅된 값을 가져옴
std::cout << f.get();
}
catch (const std::exception& exp)
{
// promise에 셋팅된 exception 값을 가져옴
std::cout << exp.what();
}
t.join();
return 0;
}
- future와 promise를 통해서 다른 쓰레드에서의 결과값을 가져올 수 있습니다. 그리고 future의 get은 단 한번만 사용할 수 있습니다.
- future를 통해서 exception 값을 셋팅하고 promise를 통해서 exception 값을 가져올 수 있습니다. 이때 exception을 promise에 셋팅할 때 반드시 exception_ptr로 셋팅해야 합니다. 그리고 promise에 exception이 셋팅되면 future의 get에서 이를 catch할 수 있습니다.
- future의 get만을 사용해서도 promise에 값이 셋팅될 때까지 기다리지만, future의 get보다 wait이 가볍기 때문에 wait을 이용해서 대기하고 get을 통해 값을 가져오는 것이 좋습니다.
shared_future
#include <iostream>
#include <future>
using namespace std;
void WorkerThread(promise<string>* p, const string *name)
{
try
{
if (name == nullptr)
{
throw runtime_error("name is nullptr");
}
p->set_value(*name + ", Hello\n");
}
catch (const std::exception& exp)
{
p->set_exception(make_exception_ptr(exp));
}
return;
}
int main()
{
promise<string> p;
shared_future<string> f = p.get_future();
string str = "devhun";
thread t(WorkerThread, &p, &str);
f.wait();
try
{
for (int i = 0; i < 10; ++i)
{
std::cout << f.get();
}
}
catch (const std::exception& exp)
{
std::cout << exp.what() << "\n";
}
t.join();
return 0;
}
- future는 get 함수를 한 번만 호출할 수 있으며, 이후 호출부터는 no state 예외가 발생됩니다. 하지만, shared_future를 사용할 경우 get을 중복하여 호출할 수 있습니다.
packaged_task
#include <iostream>
#include <future>
using namespace std;
string WorkerThread(const string *name, const string* hello)
{
this_thread::sleep_for(chrono::seconds(2));
return *name + *hello;
}
int main()
{
// return 타입과 매개변수 타입을 지정
packaged_task<string(const string*, const string*)> task(WorkerThread);
future<string> f = task.get_future();
string str1 = "devhun";
string str2 = ", Hello";
// 복사 생성이 불가능하기 때문에 move를 통해 인자로 전달해야 합니다.
thread t(move(task), &str1, &str2);
cout << f.get() << endl;
t.join();
return 0;
}
- packaged_task를 생성할 때 비동기로 실행할 함수를 매개 변수로 전달하고 함수의 리턴 타입과 매개 변수 타입에 대한 템플릿 타입을 지정합니다. 그리고 생성된 packaged_task를 이용해서 future 객체를 생성하여 이를 통해 비동기 실행에 대한 결과를 받아볼 수 있습니다.
- packaged_task는 promise의 set_value를 사용하지 않고 함수의 return 타입을 대신하여 처리할 수 있습니다.
- packaged_task는 promise의 set_exception을 사용하지 않고도 task에 대한 예외가 설정됩니다.
async
#include <iostream>
#include <future>
using namespace std;
string WorkerThread(const string *name, const string* hello)
{
this_thread::sleep_for(chrono::seconds(2));
return *name + *hello;
}
int main()
{
string str1 = "devhun";
string str2 = ", Hello";
future<string> f = async(launch::async, WorkerThread, &str1, &str2);
cout << f.get() << endl;
return 0;
}
- promise 또는 packaged_task는 비동기적으로 실행하기 위해서 thread 객체를 직접 생성하여 처리해야 했습니다. 하지만, asyn를 사용할 경우 내부적으로 쓰레드를 생성하여 매개변수로 전달한 함수를 비동기적으로 처리합니다.
lanch::async
async(launch::async, WorkerThread, &str1, &str2);
- launch::async를 전달할 경우 내부적으로 새로운 쓰레드를 생성하여 함수를 비동기적으로 처리합니다.
lanch::deferred
async(launch::deferred, WorkerThread, &str1, &str2);
- launch::deferred를 사용할 경우 별도의 쓰레드를 생성하지 않고 future 객체를 통해 get을 호출하였을 때 해당 쓰레드에서 함수를 호출하는 방식입니다.