Future

원래벌레·2022년 6월 26일
0
post-custom-banner

💎 Future

  • 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

  • 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가 아니라 불가합니다.

💍 Future 객체를 만드는 두번째 방법 promise

  • 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 객체를 통해 값을 받아와서 할 수 있게끔 한다.

💍 Future 객체를 만드는 세번째 방법 packaged_task

  • packaged_task는 future와 1:1로 연동을 하고, future.get()을 통해 결과 값을 가져오는 면에서 promise와 차이가 없는 것 같습니다. 하지만 둘에는 차이가 있다. promise의 경우 다른 쓰레드가 실행하는 함수에 promise를 넣어서 set_value를 해야만 future를 통해 값을 가져 올 수 있지만, packaged_task의 경우에는 단순히 return 값을 가져오기 때문에 쉽게 future를 통해 값을 가져 올 수 있다.
profile
학습한 내용을 담은 블로그 입니다.
post-custom-banner

0개의 댓글