future, async 는 mutex, condition variable 까지 쓰지 않고 단순한 쓰레드 처리시 유용하다

1회성 사용시에 유용 

 

ex)파일로딩

 

1) async : 원하는 함수를 비동기적으로 실행

2) promise : 결과물을 promise를 통해 future 를 통해 받아줌

3) packged_task : 원하는 함수의 실행 결과를 packged_task를 통해 future 로 받아 줌

2,3은 비슷하다

 

비동기 != 멀티쓰레드 

단지  용어 상의 차이인데 비동기는 나중에 함수를 호출하는 개념

즉 지연 호출의 개념이 있어서 완전 멀티 스레드라고 할 수는 없다 

 

 

wait_for

연결된 비동기 작업이 완료되거나 지정된 _Rel_time 시간이 경과할 때까지 차단합니다.

구문

C++복사
template <
    class _Rep,
    class _Period
>
std::future_status::future_status wait_for(
    const std::chrono::duration< _Rep, _Period>& _Rel_time ) const;

매개 변수

_Rep
틱 수를 나타내는 산술 형식입니다.

_Period
틱당 경과된 시간(초)을 나타내는 std::ratio입니다.

_Rel_time
작업이 완료될 때까지 대기하는 최대 시간입니다.

반환 값

HRESULT = NO_ERROR를

  • std::future_status::deferred 연결된 비동기 작업이 실행되고 있지 않으면
  • std::future_status::ready 연결된 비동기 작업이 완료된 경우
  • std::future_status::timeout 지정된 기간이 경과된 경우

 

#include "pch.h"
#include <iostream>
#include "CorePch.h"
#include <thread>	
#include <mutex>
#include "AccountManager.h"
#include "UserManager.h"
#include <chrono>
#include <future>
#include "windows.h"

using namespace std;

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


int main()
{
	{
		//비 동기 호출, calculate 가 호출 
		std::future<int64> future = std::async(std::launch::async, calculate);		

		std::future_status status = future.wait_for(1ms);	//1밀리 세컨동안 대기했다가 상태를 얻어와서

		//지금 스레드의 상태가 어떤 상태인지 알 수 있다
		//std::future_status::ready, std::future_status::deferred, std::future_status::timeout
		/*
		*	std::future_status::deferred 연결된 비동기 작업이 실행되고 있지 않으면

			std::future_status::ready 연결된 비동기 작업이 완료된 경우

			std::future_status::timeout 지정된 기간이 경과된 경우
		*/
		if(status == std::future_status::ready)		//ready 면 일감이 완료  된상태
		{
			
		}


		int64 sum = future.get();		//get 을 만나면 이때까지 스레드가 실행 안됐다면 대기했다가 결과 반환


	}

	return 0;
}

 

ready 면 일감이 완료된 상태

 

 

 

future.wait() == wiat_for 를 무한정 기다리는 상태

		//비 동기 호출, calculate 가 호출 
		std::future<int64> future = std::async(std::launch::async, calculate);		

		//std::future_status status = future.wait_for(1ms);	//1밀리 세컨동안 대기했다가 상태를 얻어와서
		future.wait();		//wiat_for 를 무한정 기다리는 상태
		
		//그래서  wait 을 했다가 결과를 호출하나  아래에서 get 을 호출하나 결과는 같다

		int64 sum = future.get();		//get 을 만나면 이때까지 스레드가 실행 안됐다면 대기했다가 결과 반환

 

 

 

 

 

wiat_for  추가 설명


future 는 thread에서 연산된 결과 값을 전달 받기 위해 사용된다.

wait() / get()은 thread에서 return 또한 set_value() 함수가 호출될 때까지 무한정 대기한다. 

wait_for()를 사용하면 일정 시간만큼 thread에서 값이 전달되기 기다리다, timout이 되면 block 상태가 해제된다. 

 

#include <iostream>
#include <future>
#include <thread>
#include <chrono>
 
int main()
{
    std::future<int> future = std::async(std::launch::async, [](){ 
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 8;  
    }); 
 
    std::cout << "waiting...\n";
    std::future_status status;
    do {
        status = future.wait_for(std::chrono::seconds(1));
        if (status == std::future_status::deferred) {
            std::cout << "deferred\n";
        } else if (status == std::future_status::timeout) {
            std::cout << "timeout\n";
        } else if (status == std::future_status::ready) {
            std::cout << "ready!\n";
        }
    } while (status != std::future_status::ready); 
 
    std::cout << "result is " << future.get() << '\n';
}

 

Line별로 자세히 살펴보도록 하자. 

std::future<int> future = std::async(std::launch::async, [](){ 
        std::this_thread::sleep_for(std::chrono::seconds(3));
        return 8;  
    });

std::launch::async 정책으로 async()를 수행하는데, lamda 표현식을 사용하여 thread를 생성하였다. ( 3초를 sleep하고 8을 return ) 

 

std::future_status status;
    do {
        status = future.wait_for(std::chrono::seconds(1));
        if (status == std::future_status::deferred) {
            std::cout << "deferred\n";
        } else if (status == std::future_status::timeout) {
            std::cout << "timeout\n";
        } else if (status == std::future_status::ready) {
            std::cout << "ready!\n";
        }
    } while (status != std::future_status::ready);

이 섹션에서 핵심 코드라고 할 수 있다. 

wait_for() 함수는 아래와 같은 return 값을 가진다. 

wait_for()에서 second(1)을 전달하여 1초 대기하도록 선언하였다. 앞서 본 thread에서 3초 sleep 후 8을 return 하기에 

do - while 1번째에는 timeout이 발생하게된다. (timeout 로그 출력) 

이후 do - while 조건문에서 status가 ready가 아니므로, 다시 wait_for() 함수가 호출된다. thread는 아직 3초가 지나지 않았기에 다시 한번 timeout이 발생한다. (timeout 로그 출력) 

다시 do - while 3번째에서 wait_for() 함수가 호출되어 1초를 wait하게 되는데, 이제는 thread가 3초 sleep에서 깨어나 8을 return 하게 된다. 

그래서 wait_for() 함수는 ready를 리턴하게 된다. 

do - while 문에서 status가 ready가 되어 do - while문을 빠져 나와 get() 함수를 통해 8을 출력하게 된다. 


클래스 인스턴스와 멤버 함수 호출 시

 		class ABC
		{
		public :
			int64 getalph() {
				return 10;
			}
		};

		ABC abc;

		std::future<int64> future2 = std::async(std::launch::async, &ABC::getalph, abc);

		auto result = future2.get();

결과는 10

 

 

 

std::promise 와 std::future 조합

 

스레드 만들고 전역변수 만들어 데이터 전달하기 보단 스레드 함수에서 데이터를

assign 하여 스래드 대기하는 쪽에서 데이터를 받아 볼 수 있는 기법

 

 

void promiseWorker(std::promise<string>&& promise)
{
	promise.set_value("secret message");
}

main 함수 중....
{
			//아래 코드처럼 작성하면
			//promise 에서 future 를 얻어올 수 있음
			//만약 promise 에 어떤 데이트를 세팅하면
			//future 에서 데이터를 얻어올 수 있음
			//미래에 결과물을 반환해달라는 일종의 약속(promise) 하는 것

			std::promise<string> promise;
			std::future<string> future = promise.get_future();
			//여기 까지 설정한다음
			
			//스레드를 만들어 소유권을 넘겨주면
			//즉 std::move(promise) 를 promiseWorker 스레드에 인자로 넘겨 준다
			thread t(promiseWorker, std::move(promise));

			//그리고 위 코드에서 
			//promise.set_value("secret message");
			//를 했음으로 promise 에서 이 데이터를 다음 처럼 받아 올 수 있다

			string data = future.get();

			std::cout << data;
            t.join();
}

 

promise 에서 future 를 얻어올 수 있음
만약 promise 에 어떤 데이트를 세팅하면
future 에서 데이터를 얻어올 수 있음
미래에 결과물을 반환해달라는 일종의 약속(promise) 하는 것

 

std::promise<string> promise;
std::future<string> future = promise.get_future();

여기 까지 설정한다음

스레드를 만들어 소유권을 넘겨주면
즉 std::move(promise) 를 promiseWorker 스레드에 인자로 넘겨 준다
thread t(promiseWorker, std::move(promise));

 

 

그리고 위 코드에서 
promise.set_value("secret message");
를 했음으로 promise 에서 이 데이터를 다음 처럼 받아 올 수 있다

 

string data = future.get();

std::cout << data;

 

 

결과 화면

 

 

 

 

 

코드에서 보면 move 하기 전에 promise 가 pending 상태지만 move 하고 난 이후엔 main 에서의 promise 는 소유권을 넘겨주기 때문에 empty 가 되어 더이상 지역변수 promise 를 사용하면 안된다 

즉 결과는 fugure.get()  을 통해 넘겨 받게 된다

 

이렇게 하는 이유는 실수로 여러번 get을 호출하면 문제가 되기 때문에

 

async 와 차이점이 있다면 promise 는 객체를 만들어서 여기저기 재사용이 가능하다

 

 

 

 

 

packaged_task 와 future

promise 와 유사하지만 사용이 간단한 packaged_task 가 있음

 

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

	//main 어딘가..
	{
		//promise 와 유사하지만 사용이 간단한 packaged_task 가 있음
		std::packaged_task<int64(void)> task(calculate);
		std::future<int64> future = task.get_future();

		thread t(taskWorker, std::move(task));

		std::cout << future.get() << std::endl;;

		t.join();
	}

 

 

결과는 

100000

 

 

promise 와 차이점이 있다면 동일한 함수타입에 대한 함수들에 task를 넘겨줘서 

결과물을 받아올때 유용

 

 

https://answer-me.tistory.com/32

ref : https://docs.microsoft.com/ko-kr/cpp/parallel/amp/reference/completion-future-class?view=msvc-170 

반응형

+ Recent posts