C++ future

2023/01/12

future 经常被翻译为期物,它相当于函数的契约,在需要的时候可以通过这个契约来获取函数的返回值,他的创建方式有下面三种模式,分别对应着三种不同的使用场景

Async

async 可以返回 future 对象,其传入的参数是可调用对象及其参数

#include <iostream>
#include <future>

using namespace std;

void sayHello(){
    cout << "Hello" << endl;
}

int add(int a, int b){
    return a + b;
}

int main(){
    future<void> a = async(sayHello);
    future<int> b = async(add, 2, 3);
    a.get();
    cout << b.get() << endl;
}

生成的 future 的类型根据传入可调用对象的返回值而定,一般使用 auto 自动推导。future 可以通过 wait 方法来等待结果可以用,可以使用 get 获得可调用对象的返回值。

需要注意的是在生成 future 的时候并不一定同时生成一个线程运行对应的可调用对象,这个与其工作模式有关,可以通过 async 的第一个参数来指定工作模式,如果第一个参数为 std::launch::deferred 就表示只有调用 futureget 或者 wait 函数的时候,传入给 async 的函数才真正被调用。如果传入的第一个参数为 std::launch::async 表示在生成 future 的时候立即生成一个线程来运行可调用对象。默认的模式是 std::launch::deferred | std::launch::async 表示模式是可以根据编译器来选择的。

aysnc 可以用于分治算法的多线程计算。

Packaged_task

packaged_task 的初始化与 async 一种需要提供可调用对象,packaged_task 的类型根据可调用对象的返回值类型来定

#include <iostream>
#include <future>

using namespace std;

void sayHello(){
    cout << "Hello" << endl;
}

int add(int a, int b){
    return a + b;
}

int main(){
    packaged_task<void()> task(sayHello);
    task();
}

packaged_task 本身是一个可执行对象,它执行的时候回导致内部的可执行对象一起执行。 packaged_task 有一个 get_future 方法来获取 future 对象。packaged_task 的作用就是将函数的调用与结果分离开来,一个线程用于调用,另一个线程通过 futureget 方法来获取调用的结果。

Promise

promise 提供了一种数据通信的方法

int main(){
    promise<int>pr;
    future<int>f = pr.get_future();
    thread w([&](){
        pr.set_value(0);
    });
    w.detach();
    cout << f.get() << endl;
}

promise 初始化的时候需要提供一个数据类型,这个数据类型是承若接下来要传入的数据类型,同 packaged_task 一样它有一个 get_future 方法来获取一个 future ,可以通过这个 futureget 方法来获取所存入的数据,如果没有存入数据数据则回堵塞。promise 可以通过 set_value 来存入数据。