理解了 python 的 promise 和 future,顺便再看看 c++ 的。
为什么要用 promise 和 future
此前,我们跨线程传递数据基本是使用消息队列,代码比较复杂,还要考虑线程安全问题。
future 和 promise 的作用也是在不同线程之间传递数据,代码更简洁。
promise 大概是个承诺,表示我将放数据进去,future 表示将来会有数据过来。
future
先看看 future 是如何使用的
#include <future>
#include <iostream>
using namespace std;
int factorial(int N) {
    int res = 1;
    for (int i = N; i > 1; i--) res *= i;
    return res;
}
int main() {
    future<int> f = async(factorial, 10);
    cout << f.get() << endl;
    return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
这是一个最简单的例子,async 将启动一个线程,执行函数,把参数传入,返回一个 future 对象,表示此时我们还没有获得函数执行结果,但是可以在将来拿到。
其中 async 函数省略了第一个参数,默认是
    future<int> f = async(launch::async|launch::deferred, factorial, 10);
两个值的含义是:
- launch::async自动创建一个线程,启动异步任务。
- launch::deferred线程的执行就会推迟到- future.get()方法时才会启动,如果不使用- get或者- wait,线程直接结束。
我们可以通过 get 方法获得函数执行结果,这个方法会等待函数执行完成,然后返回函数的返回值。
注意到我们这里我们直接把 factorial 的参数 10 传进去了,如果我们目前没有这个参数,想在之后传进去,就可以使用 promise 来实现。
promise
#include <future>
#include <iostream>
using namespace std;
int factorial(future<int>& f) {
    int res = 1;
    int N = f.get();
    for (int i = N; i > 1; i--) res *= i;
    return res;
}
int main() {
    promise<int> p;
    future<int> f = p.get_future();
    future<int> f2 = async(factorial, ref(f));
    p.set_value(10);
    cout << f2.get() << endl;
    return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
于是这里出现了两个 future:
- 第一个是和 promise对应的,相当于管道的两端。我们承诺将来会传数据进去,然后把对应的future(也就是管道出口)交给另一个线程。
- 第二个是 factorial返回结果的出口,同第一个例子,我们暂时拿不到。
但这里的 ref 是什么东西?其实就是引用,这里 ref(f) 的类型相当于上面的 future<int>&。
我们知道 C++ 中本来就有引用的存在,为何 C++11 中还要引入一个 std::ref?主要是考虑函数式编程(如 std::bind)在使用时,是对参数直接拷贝,而不是引用。
再看 factorial 函数里,我们可以用 future.get() 来获得将来传过来的参数,然后再继续执行。
回到主函数,我们首先通过 promise.set_value() 把数据传过去,然后我们就可以使用 get 从第二个 future 中获得结果了。
反向思考
写到这里我想到一个问题,能不能用 promise 把计算结果传回来?
可以!
#include <future>
#include <iostream>
using namespace std;
void add(promise<int>& p, int a, int b) {
    p.set_value(a + b);
}
int main() {
    promise<int> p;
    future<void> f = async(add, ref(p), 114, 514);
    cout << p.get_future().get() << endl;
    return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
呐,这就是了。
这样函数没有返回值了,我们在函数返回之前用 get 拿到结果,返回的 future<void> f 也没用了,但不写编译器不开心。