※ Rookiss님의 [C++과 언리얼로 만드는 MMORPG 게임 개발 시리즈] Part4: 게임 서버 강의를 보고 정리한 글입니다.
비동기 처리를 해주는 것들이 바로 Future, Promise, packaged_task다!
Calculate라는 함수가 있다.
int Calculate() {
int sum = 0;
for (int32 i = 0; i < 100'000; i++)
sum += i;
result = sum;
return sum;
}
이 Calculate라는 함수는 동기 실행으로 진행하게되면 오랜 시간을 소모한다. 이 함수를 Future를 통해 비동기로 실행해보자
std::future<int> future = std::async(std::launch::async/*타입*/, Calculate/*함수 이름*/);
int sum = future.get(); // 결과 값을 받아온다.
future의 타입엔 2가지가 있다.
타입 종류
1) deferred
지연해서 실행하세요. → 나중에 실행하고자 할 때 실행한다.
⇒ 왜쓰지? 나중에 커맨드 패턴 처럼 나중에 실행을 해야할 때 사용된다.
2) async
별도의 쓰레드를 만들어 함수를 실행 한다.
상태 체크
future_status status = future.wait_for(1ms);// 원하는 만큼 대기
future.wait(); // 작업이 끝날 때 까지 대기
if (status == future_status::ready) {
//TODO
}
wait_for을 사용하면 status를 얻을 수 있다!
멤버 함수에 사용
클래스 하위에있는 함수는 어떤 식으로 써야할까?
class Knight {
public:
int GetHp() { return 100; }
};
Knight knight;
std::future<int> future2 = std::async(std::launch::async, &Knight::GetHp, knight);
멤버함수의 주소, 함수를 쓸 객체를 넣어주면된다.
미래(std::future)에 결과물을 반환해준다고 약속(std::promise)하는 것
여러 쓰레드 끼리 값을 주고 받을 때 관리하기 힘든 전역 변수같은 것이 아닌 Promise를 사용한다.
std::promise<string> promise;
std::future<string> future = promise.get_future();
promise에서 그에 맞는 future를 받을 수 있다.
계약서같은 것이라고 생각하면 된다. 계약서는 나만 가지고있는 것이아닌 나와 상대방 모두 가지고 있다!
이제 상대방에게 계약서를 넘겨준다.
void PromiseWorker(std::promise<string>&& promise) {
promise.set_value("Secret Message"); // setValue!
}
thread t(PromiseWorker, std::move(promise)); // 오른 값으로 바꿔 소유권을 내가 아닌 상대방으로 바꿔준다!
t.join();
promise의 set_value를 사용하게 되면 promise에서 뽑아왔던 future에서 이를 받을 수 있다. 이것을 이용해 쓰레드 끼리 데이터를 주고받고할 수 있다!
string message = future.get();
cout << message << endl;
future.get은 한 번 호출하면 future도 더 이상 유효하지않기 때문에 중복해서 사용하는 것을 주의해야 한다!
pakaged_task는 promise와 유사하다.
선언
std::packaged_task<int(void) /*함수와 인터페이스 맞추기*/> task(Calculate);
std::future<int> future = task.get_future(); // 마찬가지로 future 가져오기
사용
void TaskWorker(std::packaged_task<int(void)>&& task) {
task(); // 그냥 받아온 함수 바로 실행
}
std::thread t(TaskWorker, std::move(task)); // 오른값으로 넘겨주기
int sum = future.get();
cout << sum << endl;
t.join();
task에 있는 함수의 반환값을 future을 통해 얻을 수 있게 된다.
그럼 이건 그냥 future를 사용하는 것이랑 뭐가달라?!
future같은 경우 future에게 넘겨주는 오직 그 일감을 위해 새로운 스레드를 사용해 비동기로 실행하게된다.
하지만 packaged_task의 경우 이미 생성 되어있는 스레드에게 일감을 던져줄 수 있다!
미묘한 차이가 있는것이다.
mutex, condition_variable까지 가지않고 단순한 애들을 처리할 수 있다.
특히, 한 번 발생하는 단발성 이벤트에 유용하다.
1) async
원하는 함수를 비동기적으로 실행
2) promise
결과물을 promise를 통해 future로 받아줌
3) packaged_task
원하는 함수의 실행 결과를 packaged_task를 통해 future로 받아줌
NICE WORK