ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • C++ future(promise, async, packaged_task)
    C++/C++ 멀티스레드 2022. 10. 15. 13:00
    728x90

    오늘은 C++의 future에 대해서 알아보겠습니다.

     

    우리는 일반적으로 코드가 실행된다고 하면, 순차적으로 실행되는 것을 생각합니다.

     

    1~10번까지의 코드가 있다면,

    1번이 실행되고 2번이

    2번이 실행되고 3번이 이렇게 말입니다.

     

    그런데 만약 1번 명령문을 수행하는데에 아주 오랜 시간이 걸리면 어떻게 될까요?

    2~10번코드는 1번 명령문이 완료되기를 기다리게 됩니다.

    아주 오랜시간동안요.

     

    하지만, 1번 명령문이 나머지 2~10번 코드에 영향을 미치는 코드가 아니라면?

    1번이 반환하는 결과가 당장 필요하지 않다면?

     

    그럴 때는 1번 명령을 어떤 스레드에게 맡겨놓고,

    1번 명령이 완료되기 전에, 나머지 2~10번을 수행할 수 있을것입니다.

     

    그리고, 이 때 1번 명령의 결과값을 저~기 7이나 8번즈음에서

    받기 위해서 사용하는 방법이 future입니다.

     

    future를 이용하기위해 제공되는 세 가지 방법 promise, async, packaged_task에 대해서 알아보겠습니다.


    Promise

    promise는 future에게 전달할 실행결과를 저장하는 것이라고 생각하면 됩니다.

     

    코드를 먼저 보면 이해가 빠를 겁니다.

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <Windows.h>
    #include <future>
    
    __int64 Calculate()
    {
    	__int64 sum = 0;
    	for (int i = 0; i < 1000000; i++)
    		sum += i;
    
    	return sum;
    }
    
    void PromiseWork(std::promise<__int64>&& promise)
    {
    	promise.set_value(Calculate());
    }
    
    void main()
    {
    	//받아올 값을 저장할 promise를 생성!
    	std::promise<__int64> promise;
    
    	//promise에 저장'될' 값을 future를 통해 출력할 수 있다.
    	std::future<__int64> future = promise.get_future();
        
        /*지금까지는 Calculate가 실행되지 않았지만, future를 통해서 그 결과를 받아오게 될것이다.*/
    
    	//Calculate를 실행하고, promise에 그 결과값을 셋팅한다.
    	//promise는 copy할 수 없고, move해야한다.
    	std::thread t(PromiseWork, std::move(promise));
    
    	//다른 작업ing...
    
    	//다른 스레드가 실행한 Calculate의 결과값을 여기서 받아온다.
    	//만약 실행이 끝나지 않았으면 block하고 끝날 때 까지 대기한다.
    	__int64 sum = future.get();
    	cout << sum << endl;
    
    	t.join();
    }
    • 참고사항
      • future는 get을 한 번만 호출할 수 있다.
      • promise는 set_value()를 한 번만 호출할 수 있다.

     


    Async

     

    번역하면, 비동기라는 의미인데

    실핸순서가 선형이 아니라는 뜻입니다.

     

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <Windows.h>
    #include <future>
    
    __int64 Calculate()
    {
    	cout << "start" << endl;
    	__int64 sum = 0;
    	for (int i = 0; i < 10000000; i++)
    		sum += i;
    
    	cout << "ends" << endl;
    	return sum;
    }
    
    void main()
    {
    	std::future<__int64> future = std::async(std::launch::async, Calculate);
    	//std::future<__int64> future = std::async(std::launch::deferred, Calculate);
    	//std::future<__int64> future = std::async(Calculate);
    
    	//다른 작업ing...
    
    	__int64 sum = future.get();
    
    	cout << sum << endl;
    }

    오히려 코드는 간단합니다.

     

    async의 policy로 넘겨주는 인자에 대해서 알아보겠습니다.

    • std::launch::async : 호출하는 순간 새로운 스레드를 하나 생성해서, 함수를 실행합니다.
    • std::launch::deferred : future.get()을 하는 순간, 스레드를 생성하지 않고, 함수를 실행합니다.
    • default : async | deferred : C++런타임이 알아서 결정합니다.

    packaged_task

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <Windows.h>
    #include <future>
    
    __int64 Calculate()
    {
    	cout << "start" << endl;
    	__int64 sum = 0;
    	for (int i = 0; i < 10000000; i++)
    		sum += i;
    
    	cout << "ends" << endl;
    	return sum;
    }
    
    void TaskWorker(std::packaged_task<__int64(void)>&& task)
    {
    	task();
    }
    
    void main()
    {
    	//return type, parameter type을 넘겨준다.
    	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();
    }

    packaged_task는 promise를 자동으로 생성하고, 호출한 함수의 결과를  promise에 저장해준다.

     


    언제쓰는가?

    대표적으로 예외처리할 때 스레드끼리 exception을 주고받을 때 활용할 수 있다.

     

    #include <iostream>
    #include <thread>
    #include <mutex>
    #include <Windows.h>
    #include <future>
    
    int calculate()
    {
    	throw runtime_error("Exception thrown from calculate()");
    }
    
    int main()
    {
    	std::future<int> future = std::async(std::launch::async, calculate);
    
    	//다른 작업ing...
    
    	try{
    		int result = future.get();
    		cout << result << endl;
    	}
    	catch (exception e) {
    		cout << e.what() << endl;
    	}
    }

     

    728x90

    'C++ > C++ 멀티스레드' 카테고리의 다른 글

    TLS(Thread Local Storage)  (0) 2022.11.08
    C++ Conditional Variable(조건변수)  (0) 2022.10.14
    C++ 멀티스레드 동기화, 이벤트 사용법  (0) 2022.10.12
    C++, Sleep을 이용한 Lock구현  (0) 2022.10.10
    C++ SpinLock구현  (0) 2022.10.09

    댓글

Designed by Tistory.