C++ 비동기 프로그래밍

김 무무 ㅣ 2024. 10. 15. 13:05

1. std::future와 std::promise

C++11에서 도입된 비동기 프로그래밍을 위한 도구

 

std::future : 비동기 작업의 결과를 나타내는 객체. 미래의 어느 시점에 값이 설정될 것임을 나타낸다.

std::promise : future에 값을 설정하는 쓰기 가능한 끝점

 

이 둘은 서로 쌍을 이루어 동작하며, 다른 스레드에서 실행되는 작업의 결과를 안전하게 전달할 수 있게 한다.

 

ex)

#include <iostream>
#include <future>
#include <thread>
using namespace std;

void SetValue(promise<int>& p) {
    this_thread::sleep_for(chrono::seconds(2));
    p.set_value(10);
}

int main() {
    promise<int> p;
    future<int> f = p.get_future();
    thread t(set_value, ref(p));

    cout << "대기중.." << endl;
    cout << "값: " << f.get() << endl;

    t.join();
}

 

 

2. std::packaged_task

함수나 callable 객체를 래핑하여 그 실행 결과를 future 객체를 통해 얻을 수 있게 해주는 클래스 템플릿

 

ex)

#include <iostream>
#include <future>
#include <thread>
using namespace std;

int Computation(int a, int b) {
    this_thread::sleep_for(chrono::seconds(2));
    return a + b;
}

int main() {
    packaged_task<int(int, int)> task(ComputeSum);
    future<int> f = task.get_future();

    thread t(move(task), 10, 20);

    cout << "대기중.." << endl;
    cout << "값 : " << f.get() << endl;

    t.join();
}

 

 

3. std::async

std::async는 함수를 비동기적으로 실행하고 결과를 future 객체로 반환하는 인터페이스를 제공한다.

 

실행정책:

std::launch::async : 새 스레드에서 함수를 실행

std::launch::deferred : 결과가 필요할 때까지 함수 실행을 지연

std::launch::async | std::launch::deferred : 실행 방식을 시스템이 결정

 

ex)

#include <iostream>
#include <future>
#include <chrono>
using namespace std;

long Fibonacci(unsigned int n) {
    if (n < 2) return n;
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}

int main() {
    cout << "Fibonacci 계산" << endl;

    auto start = chrono::high_resolution_clock::now();
    future<long> f = async(launch::async, Fibonacci, 40);

    cout << "-다른 작업 실행-" << endl;

    long result = f.get();

    auto end = chrono::high_resolution_clock::now();
    chrono::duration<double> diff = end - start;

    cout << "\n결과\n" << "값 : " << result << endl;
    cout << "시간 : " << diff.count() << " 초" << endl;
}

 

위 예제에서 Fibonacci 함수는 비동기로 실행되며, 메인 스레드는 결과를 기다리는 동안 다른 작업을 수행할 수 있다.

 

 

4. std::future의 추가 기능

std::future 클래스의 메서드:

 

wait() : 결과가 준비될 때까지 블록

wait_for() : 지정된 시간 동안 결과를 기다림

wait_until() : 지정된 시간까지 결과를 기다림

valid() : future 객체가 공유 상태와 연관되어 있는지 확인

 

ex)

#include <iostream>
#include <future>
#include <chrono>
using namespace std;

int Computation() {
    this_thread::sleep_for(chrono::seconds(3));
    return 10;
}

int main() {
    auto future = async(launch::async, Computation);

    cout << "대기중.." << endl;

    future_status status = future.wait_for(chrono::seconds(2));

    if (status == future_status::ready) {
        cout << "값 : " << future.get() << endl;
    }
    else if (status == future_status::timeout) {
        cout << "결과 대기중.." << endl;
        cout << "값 : " << future.get() << endl;
    }
}

 

 

5. std::shared_future

std::future와 달리 복사가 가능하며 여러 스레드에서 동일한 비동기 결과를 기다릴 수 있다.

 

ex)

#include <iostream>
#include <future>
#include <vector>
using namespace std;

int SetValue() {
    this_thread::sleep_for(chrono::seconds(2));
    return 10;
}

void Print(shared_future<int> f, int id) {
    cout << "스레드 " << id << " 대기중.." << endl;
    cout << "스레드 " << id << " 값 : " << f.get() << endl;
}

int main() {
    promise<int> p;
    shared_future<int> shared_future = p.get_future().share();

    vector<thread> threads;
    for (int i = 0; i < 3; ++i) {
        threads.emplace_back(Print, shared_future, i);
    }

    thread th( 
        [&p](){p.set_value(SetValue());}
    );

    for (auto& t : threads) {
        t.join();
    }
    th.join();
}

'멀티스레딩' 카테고리의 다른 글

고급 동기화 기법  (1) 2024.10.16
Condition Variable  (0) 2024.10.16
스핀락(Spinlock)  (0) 2024.10.12
데드락(Deadlock)  (1) 2024.10.07
동기화 기법: Mutex  (0) 2024.10.05