把 std::async、std::packaged_task、std::promise 三个放在一起来说,是因为它们都可以返回一个 std::future 对象。简单来说,当某个线程需要等待一个特定的一次性事件(one-off event),它可以用一个 “future” 来表示这个事件。
std::async#
有的时候可能你需要做一个花费时间比较长的计算,但是计算结果不是立刻需要。这个时候就可以用一个新的线程来做这个计算。这里比较关键的问题是如何将新线程的计算结果传回当前线程,因为 std::thread 并没有提供一个类似的机制。
这个时候就需要 std::async 登场了。
1
2#include <future>
3#include <iostream>
4int find_the_answer_to_ltuae();
5void do_other_stuff();
6int main()
7{
8 std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
9 do_other_stuff();
10 std::cout<<"The answer is "<<the_answer.get()<<std::endl;
11}当然,也可以像向 std::thread 包装的 thread function 中传参数一样,向 std::async 中传参数,如下:
1#include <string>
2#include <future>
3struct X
4{
5 void foo(int,std::string const&);
6 std::string bar(std::string const&);
7};
8X x;
9auto f1=std::async(&X::foo,&x,42,"hello"); // 调用p->foo(42, "hello"),p是指向x的指针
10auto f2=std::async(&X::bar,x,"goodbye"); // 调用tmpx.bar("goodbye"), tmpx是x的拷贝副本
11struct Y
12{
13 double operator()(double);
14};
15Y y;
16auto f3=std::async(Y(),3.141); // 调用tmpy(3.141),tmpy通过Y的移动构造函数得到
17auto f4=std::async(std::ref(y),2.718); // 调用y(2.718)
18X baz(X&);
19std::async(baz,std::ref(x)); // 调用baz(x)
20class move_only
21{
22public:
23 move_only();
24 move_only(move_only&&)
25 move_only(move_only const&) = delete;
26 move_only& operator=(move_only&&);
27 move_only& operator=(move_only const&) = delete;
28
29 void operator()();
30};
31auto f5=std::async(move_only()); // 调用tmp(),tmp是通过std::move(move_only())构造得到
此外,std::async 还有一个可选参数,值为 std::launch::deferred 或 std::launch::async 或 std::launch::deferred|std::launch::async,第三种为默认参数。
std::launch::async 表示该 task 会立即在一个新的 thread 上执行。
std::launch::deferred 表示该 task 不会被立刻执行,而是在调用 get() 或者 wait() 的时候才会执行。这里需要注意的是,调用 get() 或者 wait() 的线程,可以和调用 async() 属于同一个线程,也可以属于不同线程。
笼统来说,std::launch::deferred 是一种更为灵活的方式。具体请参考 cppreference_async。
std::packaged_task#
std::packaged_task 有点类似 std::function,把要在其他线程执行的对象封装了起来。
1#include <iostream>
2#include <cmath>
3#include <thread>
4#include <future>
5#include <functional>
6
7// unique function to avoid disambiguating the std::pow overload set
8int f(int x, int y) { return std::pow(x,y); }
9
10void task_lambda()
11{
12 std::packaged_task<int(int,int)> task([](int a, int b) {
13 return std::pow(a, b);
14 });
15 std::future<int> result = task.get_future();
16
17 task(2, 9);
18
19 std::cout << "task_lambda:\t" << result.get() << '\n';
20}
21
22void task_bind()
23{
24 std::packaged_task<int()> task(std::bind(f, 2, 11));
25 std::future<int> result = task.get_future();
26
27 task();
28
29 std::cout << "task_bind:\t" << result.get() << '\n';
30}
31
32void task_thread()
33{
34 std::packaged_task<int(int,int)> task(f);
35 std::future<int> result = task.get_future();
36
37 std::thread task_td(std::move(task), 2, 10);
38 task_td.join();
39
40 std::cout << "task_thread:\t" << result.get() << '\n';
41}
42
43int main()
44{
45 task_lambda();
46 task_bind();
47 task_thread();
48}与 std::async 相比,std::packaged_task 不会直接执行任务,而是需要显式调用,因此可以做到将执行函数的时机和获取 future 的时机分离。
1std::packaged_task<int()> task(sleep);
2
3auto f = task.get_future();
4task(); // invoke the function
5
6// You have to wait until task returns. Since task calls sleep
7// you will have to wait at least 1 second.
8std::cout << "You can see this after 1 second\n";
9
10// However, f.get() will be available, since task has already finished.
11std::cout << f.get() << std::endl;
12
13
14
15
16auto f = std::async(std::launch::async, sleep);
17std::cout << "You can see this immediately!\n";
18
19// However, the value of the future will be available after sleep has finished
20// so f.get() can block up to 1 second.
21std::cout << f.get() << "This will be shown after a second!\n";std::promise#
第一次用 C++11 写多线程就是用的 promise,参见 promise && future learning notes。
个人认为 std::promise 与 std::async 或者 std::packaged_task 最明显的区别是,它的值不是函数的返回值,而是可以通过 set_value 来设置,因此更加灵活。
需要注意的是,std::promise 不支持拷贝构造,因此需要使用 std::move 或者传 promise 的指针。
In summary#
- async:提供最高层次的抽象。如果你不需要控制线程的运行时机,就选这个。
- packaged_task:抽象层次比
async低。如果你需要控制线程的运行时机,且线程执行的结果即目标结果时,选这个。- promise:抽象层次最低。当你想在线程中设置目标结果的值,选这个。
参考资料: