C++ 게임 서버 프로그래밍: 비동기 처리를 위한 std::future, std::promise, std::packaged_task 이해하기

나무에물주기·2023년 6월 12일
1
post-thumbnail

C++에서는 std::future, std::promise, std::packaged_task 등의 클래스를 통해 비동기 처리를 수행할 수 있습니다. 이들 클래스의 사용법과 각각 어떤 상황에 적합한지에 대해 알아보겠습니다.

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>
#include <atomic>
#include <mutex>
#include <future>

우선, 비동기 프로그래밍에 필요한 헤더 파일들을 추가해줍니다. 이 중에서도 future 헤더 파일이 가장 중요하며, 이를 통해 std::future, std::promise, std::packaged_task 등의 클래스를 사용할 수 있습니다.

std::future의 이해와 사용법

std::future는 어떤 작업의 결과를 담는 컨테이너로 생각할 수 있습니다. 이 객체는 비동기 작업을 수행하고, 그 결과를 담아 두었다가 나중에 필요할 때 사용할 수 있도록 합니다.

int64 Calculate()
{
	int64 sum = 0;

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

	result = sum;

	return sum;
}

우선, Calculate라는 함수를 통해 일련의 작업을 수행하고 그 결과를 반환하는 것을 볼 수 있습니다. 이 결과를 std::future를 이용해 저장하고 관리하겠습니다.

// std::future 사용법
{
	std::future<int64> future = std::async(std::launch::async, Calculate);

	int64 sum = future.get(); // 여기서 결과를 가져옴
}

std::async 함수를 통해 비동기 작업을 시작하고, 그 결과를 std::future 객체에 담습니다. 그리고 get 함수를 통해 그 결과를 가져옵니다. 이렇게 하면 Calculate 함수가 비동기적으로 실행되며, 그 결과는 필요할 때까지 future에 보관됩니다.


-- 참고 -> std::async의 이해와 사용법

std::async는 비동기 작업을 위한 함수를 제공하는데, 이 함수를 사용하면 새로운 스레드를 생성하여 함수나 함수 객체를 비동기적으로 실행할 수 있습니다.

위 코드에서 std::async 함수는 Calculate 함수를 비동기적으로 실행하고, 그 결과를 std::future 객체에 저장합니다. std::future::get 메서드를 호출하면 해당 비동기 작업의 결과를 가져올 수 있습니다.

다만, std::future::get 함수는 결과가 준비될 때까지 현재 스레드를 블록합니다. 즉, 해당 작업이 완료될 때까지 다른 작업을 수행하지 못하게 만듭니다. 따라서, 비동기 작업의 결과가 필요한 시점에 get 함수를 호출하는 것이 중요합니다.


std::promise의 이해와 사용법

std::promisestd::future와 함께 사용되는 클래스로, 값을 생성하는 쪽에서 사용합니다. std::promise는 값을 생성하고, 그 값은 std::future를 통해 사용됩니다

void PromiseWorker(std::promise<string>&& promise)
{
	promise.set_value("비밀 메시지");
}

// std::promise 사용법
{
	std::promise<string> promise;
	std::future<string> future = promise.get_future();

	thread t(PromiseWorker, std::move(promise));

	string message = future.get();
	cout << message << '\n';

	t.join();
}

이 예제에서는 PromiseWorker 함수를 통해 std::promise에 값을 설정하고, 이 값을 std::future를 통해 가져오는 것을 볼 수 있습니다. 즉, std::promise는 결과를 생성하고, std::future는 그 결과를 소비하는 역할을 합니다.

std::packaged_task의 이해와 사용법

std::packaged_task는 함수나 콜러블 객체를 받아 그 결과를 std::future에 저장하는 역할을 합니다. 이 클래스를 이용하면 함수의 실행을 나중으로 미룰 수 있습니다.

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

// std::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 << '\n';

	t.join();
}

위의 예제에서는 Calculate 함수를 std::packaged_task에 담고, 이를 TaskWorker 함수에 전달하여 실행시키는 것을 볼 수 있습니다. 이 때 Calculate 함수의 결과는 std::packaged_task를 통해 std::future에 저장됩니다.

profile
개인 공부를 정리함니다

0개의 댓글