Modern C++ - future, promise, asyn

진경천·2024년 3월 22일
0

C++

목록 보기
90/90

std::future, std::promise

#include <future>

std::future<[type]> f;
std::promise<[type]> p;

미래에 쓰레드가 원하는 데이터를 돌려주겠다는 약속이이라고 볼수 있다.
promise가 future한테 값을 보내준다고 본다.

#include <iostream>
#include <future>
#include <chrono>

using namespace std::chrono_literals;

using std::cout;
using std::endl;

int main() {
	std::promise<int> pro;
	std::future<int> fut = pro.get_future();

	pro.set_value(10);
	cout << fut.get() << endl;
}

실행 결과

10

future에서 get을 호출하면 설정된 객체가 이동되기 대문에 get을 두번 호출할 수 없다.
set_value와 get()은 한번만 사용가능
promise가 값을 주지 않으면 실행 불가

thread에 응용

promise는 producer 역할을 수행하고, futuer는 consumer 역할을 수행한다고 보면 되기 때문에 thread로 응용을 할 수 있다.

#include <iostream>
#include <future>
#include <chrono>

using namespace std::chrono_literals;

using std::cout;
using std::endl;

int main() {
	std::promise<int> pro;
	std::future<int> fut = pro.get_future();

	std::thread th([](std::promise<int> pro) {
		std::this_thread::sleep_for(2s);
		pro.set_value(10);
		}, std::move(pro));

	cout << fut.get() << endl;
	th.join();
}

실행 결과

10

예외 전달

#include <iostream>
#include <future>
#include <chrono>

using namespace std::chrono_literals;

using std::cout;
using std::endl;

int main() {
	std::promise<int> pro;
	std::future<int> fut = pro.get_future();

	std::thread th([](std::promise<int> pro) {
		try {
			throw std::runtime_error("err");
		}
		catch (...) {
			std::exception_ptr e = std::current_exception();
			pro.set_exception(e);
		}
	}, std::move(pro));

	try {
		cout << fut.get() << endl;
	}
	catch (const std::runtime_error& err) {
		cout << err.what() << endl;
	}
	th.join();
}

실행 결과

err

set_exception에는 예외 객체가 아닌 catch된 예외에 관한 정보를 반환하는 exception_ptr을 전달해야 한다.

std::packaged_task

packaged_task를 이용해 위와 같은 promise -> future 패턴을 비동기적 함수의 리턴값에 간단히 적용할 수있다.

packaged_task에 전달된 함수가 리턴할 때, 반환값을 promise에 set_value를 하고 예외가 있는 경우에는 set_exception을 한다.

#include <iostream>
#include <future>
#include <chrono>

using namespace std::chrono_literals;

using std::cout;
using std::endl;

int main() {
	std::packaged_task<int(int)> task([](int value) {
		if (value < 0)
			throw std::runtime_error("err");

		return value;
		});

	std::future<int> fut = task.get_future();

	std::thread th(std::move(task), -1);

	try {
		cout << fut.get() << endl;
	}
	catch (const std::runtime_error& err) {
		cout << err.what() << endl;
	}
}

실행 결과

err

std::async

std::async([launch], [함수], [인자])

std::async에 함수를 전달한다면 기존의 promisepackaged_task와 달리 스레드를 만들어서 해당 함수를 비동기적으로 실행하고 그 결과값을 future에 전달한다.

#include <iostream>
#include <future>
#include <chrono>

using namespace std::chrono_literals;

using std::cout;
using std::endl;

int main() {
	cout << "thread ID : " << std::this_thread::get_id() << endl;
	std::future<int> fut = std::async(std::launch::async | std::launch::deferred, [](int value) {
		cout << "thread ID : " << std::this_thread::get_id() << endl;
		if (value < 0)
			throw std::runtime_error("err");

		return value;
		}, -1);

	try {
		cout << fut.get() << endl;
	}
	catch (const std::runtime_error& err) {
		cout << err.what() << endl;
	}
}

실행 결과

thread ID : 51492
thread ID : 46868
err

std::launch::async : 스레드를 바로 생성해 인자로 전달된 함수를 실행
std::launch::deferred : 새로운 스레드를 생성하지 않고 future.get()이 호출 되었을 때 실행.

위 두가지는 생략이 가능하고 생략할 경우 std::launch::deferred | std::launch::async 로 대체된다.

위 코드 결과값에서 새로운 스레드가 생성된 것을 확인할 수 있다.

profile
어중이떠중이

0개의 댓글