dh_0e

[C++/Game Server] future, promise, packaged_task 본문

C++/Game Server

[C++/Game Server] future, promise, packaged_task

dh_0e 2025. 8. 8. 11:14

std::future

  • 무겁게 스레드를 직접 다루지 않고 미래의 값을 약속받는 방식
  • JavaScript(node.js)의 async/await(비동기) 방식과 유사
  • <future> 헤더 필요
std::future<int64> future = std::async(std::launch::async, Calculate);
  • launch 정책과 실행할 함수를 입력값으로 사용

std::launch 정책

1) std::launch::deferred

  • lazy evaluation(지연 실행)
  • 요청 시점까지 대기(lazy evaluation), 스레드 생성 X
  • main() -> 요청 -> 실행(호출)

2) std::launch::async

  • 즉시 별도 스레드로 실행
  • main() -> 별도의 스레드로 병렬 작업 실행 

사용 예시

int Calculate() {
	int sum = 0;
	for (int i = 0; i < 100'000; i++) {
		sum += i;
	}

	return sum;
}

int main()
{
	{
		std::future<int> future = std::async(std::launch::async, Calculate);
			
		int sum = future.get();
	}

	return 0;
}

 

future.get();

  • future 결과물을 반환
  • 한 번 실행하면 future가 empty가 됨 (이후 호출시 오류)

future.wait_for(1ms);

  • std::future_status 형식 반환
  • 작업이 끝났는지 1초 동안 기다렸다 확인
  • future.wait(): 바로 확인
class Knight
{
public:
	int GetHp() { return 100; }
};

Knight knight;
std::future<int> future2 = std::async(std::launch::async, &Knight::GetHp, knight);
  • 이런 식으로 객체(class)의 멤버 함수도 실행할 수 있음
  • 객체에 의존적으로 호출이 되기 때문에 객체가 포함되어야 함

 

std::promise

  • future에 값을 전달할 수 있도록 약속하는 객체
  • 스레드 간 통신에서 유용하게 사용

사용 예시

void PromiseWorker(std::promise<string>&& promise)
{
	promise.set_value("Secret Message");
}

int main()
{
	{
		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();
	}
    
    return 0;
}
  • promise.set_value()를 호출하면 future.get()에서 결과를 받을 수 있음
  • 전역 변수 없이 스레드 간 값 전달이 가능함

 

std::packaged_task

  • promise는 값이었다면 packaged_task는 함수(작업 단위)를 future로 감싸는 객체
  • promise와 구조는 비슷하지만 값을 직접 전달하는 게 아니라, 함수 실행 결과를 전달함
int Calculate() {
	int sum = 0;
	for (int i = 0; i < 100'000; i++) {
		sum += i;
	}

	return sum;
}

void TaskWorker(std::packaged_task<int(void)>&& task)
{
	task();
}

int main()
{
	{
		std::packaged_task<int(void)> task(Calculate);
		std::future<int> future = task.get_future();

		std::thread t(TaskWorker, std::move(task));
		
		int sum = future.get();
		cout << sum << endl;
	}

	return 0;
}

 

 

std::async vs std::promise vs std::packaged_task

항목 std::async std::promise std::packaged_task
주 역할 함수 실행 후 결과 받기 직접 값을 넘기기 함수 자체를 포장해 넘기기
Future 생성 방식 내부에서 생성됨 get_future()로 획득 get_future()로 획득
실행 방식 자동 비동기/지연 실행 사용자가 직접 실행 사용자가 직접 실행
thread 필요 여부 옵션에 따라 다름 직접 thread 생성해야 함 직접 thread 생성해야 함

 

정리

 

  • std::future: 미래의 결과값을 보장받는 객체
  • std::async: 함수 실행과 future를 연결
  • std::promise: 값을 외부에서 전달해주는 약속
  • std::packaged_task: 함수 전체를 포장해서 future로 전달
  • 모두 mutex, condition_variable를 사용하기엔 단순한 작업을 처리할 수 있는, 특히 한 번 발생하는 이벤트에 유용한 객체들이다.