future는 mutex, condition_variable까지 가지 않고 단순한 애들을 처리 할 수 있는 특히나 한 번 발생하는 이벤트에 유용합니다.
future를 처리하는 방법은 세가지가 있습니다.
(1) async : 원하는 함수를 비동기적으로 실행
(2) promise : 결과물을 promise를 통해 future로 받아줌
(3) packaged_task : 원하는 함수의 실행 결과를 packaged_task를 통해 future로 받아줌
#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
#include <mutex>
#include <windows.h>
#include <future>
int64 Calculate()
{
int64 sum = 0;
for (int32 i = 0; i < 100'000; i++) {
sum += i;
}
return sum;
}
void PromiseWorker(std::promise<string>&& promise)
{
promise.set_value("Secret Message");
}
void TaskWorker(std::packaged_task<int64(void)>&& task)
{
task();
}
int main()
{
//future
{
std::future<int64> future = std::async(std::launch::async, Calculate);
int64 sum = future.get();
class Knight
{
public:
int64 GetHp() { return 100; }
};
Knight knight;
std::future<int64> future2 = std::async(std::launch::async, &Knight::GetHp, knight);
}
//promise
{
std::promise<string> promise;
std::future<string> future = promise.get_future();
thread t(PromiseWorker, std::move(promise));
string message = future.get();
cout << message << endl;
t.join();
}
//packaged_task
{
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();
}
};
async 방식은 동기적으로 처리하던 코드를 비동기식으로 처리하여 main의 부담을 줄여준다.
std::future<int64> future = std::async(std::launch::async,Funtion);
위의 명령어에서 템플릿으로 감싸진 부분은 함수의 반환값이 들어가는 부분이다.
async는 인수로 모드와 함수를 받습니다. 여기서 모드의 종류는 총 세가지가 있습니다.
(1) std::launch::async : 해당 모드는 자동적으로 쓰레드를 생성하여 쓰레드가 다음 인수로 받은 함수를 실행하게 해줍니다.
(2) std::launch::deferred : lazy evaluation 즉 지연 실행으로 인수로 받은 함수를 현재 실행하지 않고 후에 future 오브젝트의 get 메소드를 받을 때, 함수를 실행합니다.
(3) std::launch::async | deferred : 이 경우는 둘 중 하나의 모드를 선택하여 함수를 처리 하게 합니다.
이렇게 async를 통하여 등록된 함수가 끝났는지 확인을 하는 방법이 있습니다.
std::future_status status=future.wait_for(1ms);
if(status == future_status::ready) {
//TODO
}
위의 명령어 내용을 통하여 현재 future에 등록된 함수가 끝났는지 안끝났는지를 확인 할 수 있습니다. wait_for() 메소드는 아규먼트로 받은 시간 만큼 future가 끝날 때 까지 기다립니다. 만약에 future가 시간이 다 되어도 끝나지 않으면, timeout을 리턴하고, 완료가 됐다면 ready를 리턴합니다. 여기서 wait() 메소드 또는 get() 메소드를 쓰게되면 ready 상태가 될 때까지 기다립니다.
future는 인수로 받는 함수로 클래스의 메소드 또한 받을 수 있습니다. 자세한 내용은 코딩 예제를 확인하시면 됩니다.
&&참조는 r-value 즉 1,2,3,4,5와 같은 자연수 같은 것들에 대하여 별명을 지정하여
i-value 즉 레퍼런스 변수 값으로 저장하는데 사용된다.
ex)
int& ref = 5(x)
int&& ref2 = 5(o)
int&& ref3 = 5(o)
int&& ref4 = ref3(x) -> ref3은 i-value임, r-value가 아니라 불가합니다.
promise는 future와 1:1로 연동한다. 이렇게 연동된 promise는 move를 통하여 다른 thread로 이동 시킬 수 있다. move는 i-value의 값을 r-value로 바꿔주는 역할을 한다. 또한 move를 꼭 해줘야 하는 이유는 promise나 packaged_task 같은 경우는 복사가 불가능하다. 따라서 move를 이용해주어야 한다. 즉 이렇게 다른 thread로 promise를 이동시켜, 처리된 결과를 main 쓰레드에서 future.get()을 통하여 가져 올 수 있습니다.
promise 방법을 통하여 다른 쓰레드와 상호 작용을 할 때 전역변수를 이용하기 보다는 promise - future 짝을 맞추어 future 객체를 통해 값을 받아와서 할 수 있게끔 한다.