future & promise 사용법


  • promise 객체를 통해 자신과 연결된 future 객체를 생성할 수 있습니다. promise 객체에 값이 셋팅되었을 때 future를 통해서 해당 값을 가져올 수 있습니다.




future & promise 사용법 예시


#include <iostream>
#include <future>

using namespace std;

void WorkerThread(promise<string>* p, const string *name)
{
    try
    {
        // name이 nullptr일 경우 throw
        if (name == nullptr)
        {
            throw runtime_error("name is nullptr");
        }

        // promise에 값을 셋팅
        p->set_value(*name + ", Hello\n");
    }
    catch (const std::exception& exp)
    {
        // promise에 exception_ptr을 셋팅
        p->set_exception(make_exception_ptr(exp));
    }

    return;
}

int main()
{
    promise<string> p;

    // 자신과 연결된 promise 객체를 생성
    future<string> f = p.get_future();

    string str = "devhun";

    thread t(WorkerThread, &p, nullptr);

    f.wait();

    try
    {
        // promise에 셋팅된 값을 가져옴
        std::cout << f.get();
    }
    catch (const std::exception& exp)
    {
        // promise에 셋팅된 exception 값을 가져옴
        std::cout << exp.what();
    }

    t.join();

    return 0;
}

  • future와 promise를 통해서 다른 쓰레드에서의 결과값을 가져올 수 있습니다. 그리고 future의 get은 단 한번만 사용할 수 있습니다.

  • future를 통해서 exception 값을 셋팅하고 promise를 통해서 exception 값을 가져올 수 있습니다. 이때 exception을 promise에 셋팅할 때 반드시 exception_ptr로 셋팅해야 합니다. 그리고 promise에 exception이 셋팅되면 future의 get에서 이를 catch할 수 있습니다.

  • future의 get만을 사용해서도 promise에 값이 셋팅될 때까지 기다리지만, future의 get보다 wait이 가볍기 때문에 wait을 이용해서 대기하고 get을 통해 값을 가져오는 것이 좋습니다.




shared_future


#include <iostream>
#include <future>

using namespace std;

void WorkerThread(promise<string>* p, const string *name)
{
    try
    {
        if (name == nullptr)
        {
            throw runtime_error("name is nullptr");
        }

        p->set_value(*name + ", Hello\n");
    }
    catch (const std::exception& exp)
    {
        p->set_exception(make_exception_ptr(exp));
    }

    return;
}

int main()
{
    promise<string> p;

    shared_future<string> f = p.get_future();

    string str = "devhun";

    thread t(WorkerThread, &p, &str);

    f.wait();

    try
    {
        for (int i = 0; i < 10; ++i)
        {
            std::cout << f.get();
        }
    }
    catch (const std::exception& exp)
    {
        std::cout << exp.what() << "\n";
    }

    t.join();

    return 0;
}

  • future는 get 함수를 한 번만 호출할 수 있으며, 이후 호출부터는 no state 예외가 발생됩니다. 하지만, shared_future를 사용할 경우 get을 중복하여 호출할 수 있습니다.




packaged_task


#include <iostream>
#include <future>

using namespace std;

string WorkerThread(const string *name, const string* hello)
{
    this_thread::sleep_for(chrono::seconds(2));

    return *name + *hello;
}

int main()
{
    // return 타입과 매개변수 타입을 지정
    packaged_task<string(const string*, const string*)> task(WorkerThread);

    future<string> f = task.get_future();

    string str1 = "devhun";
    string str2 = ", Hello";

    // 복사 생성이 불가능하기 때문에 move를 통해 인자로 전달해야 합니다.
    thread t(move(task), &str1, &str2);

    cout << f.get() << endl;

    t.join();

    return 0;
}

  • packaged_task를 생성할 때 비동기로 실행할 함수를 매개 변수로 전달하고 함수의 리턴 타입과 매개 변수 타입에 대한 템플릿 타입을 지정합니다. 그리고 생성된 packaged_task를 이용해서 future 객체를 생성하여 이를 통해 비동기 실행에 대한 결과를 받아볼 수 있습니다.

  • packaged_task는 promise의 set_value를 사용하지 않고 함수의 return 타입을 대신하여 처리할 수 있습니다.

  • packaged_task는 promise의 set_exception을 사용하지 않고도 task에 대한 예외가 설정됩니다.




async


#include <iostream>
#include <future>

using namespace std;

string WorkerThread(const string *name, const string* hello)
{
    this_thread::sleep_for(chrono::seconds(2));

    return *name + *hello;
}

int main()
{
    string str1 = "devhun";
    string str2 = ", Hello";

    future<string> f = async(launch::async, WorkerThread, &str1, &str2);

    cout << f.get() << endl;

    return 0;
}

  • promise 또는 packaged_task는 비동기적으로 실행하기 위해서 thread 객체를 직접 생성하여 처리해야 했습니다. 하지만, asyn를 사용할 경우 내부적으로 쓰레드를 생성하여 매개변수로 전달한 함수를 비동기적으로 처리합니다.




lanch::async


async(launch::async, WorkerThread, &str1, &str2);

  • launch::async를 전달할 경우 내부적으로 새로운 쓰레드를 생성하여 함수를 비동기적으로 처리합니다.




lanch::deferred


async(launch::deferred, WorkerThread, &str1, &str2);

  • launch::deferred를 사용할 경우 별도의 쓰레드를 생성하지 않고 future 객체를 통해 get을 호출하였을 때 해당 쓰레드에서 함수를 호출하는 방식입니다.




'Programming Language > C, C++' 카테고리의 다른 글

C++ condition_variable 사용 방법  (0) 2023.04.15
C++ unique_lock 사용방법  (0) 2023.04.15
C++ mutex와 lock_guard  (0) 2023.04.15
C++ 다중 상속(Multiple inheritance)이란?  (0) 2023.03.26
C++ 람다(lambda)란?  (0) 2023.03.25

+ Recent posts