# std::async/std::promise和std::packaged_task
# std::async
# 介绍
std::async
是一个执行异步任务的函数模板,其函数运行在从线程池获取的独立线程上。
std::async
的放回值是一个std::future
对象,通过get
方法获取函数的返回值。
std::async
支持的运行策略有:
std::launch::async
(opens new window),std::async
可以在后台启动一个新的线程(或者从线程池中获取一个线程),异步执行指定的函数f
。它返回一个std::future
对象,最终将保存函数调用的结果。std::launch::deferred
(opens new window),std::async
还支持延迟求值。如果设置了std::launch::deferred
标志,函数f
不会立即执行,而是在调用std::future
的get()
或wait()
方法时才执行。
函数f
运行结束和std::async
返回的future
对象使用的共享状态值修改是串行的。
# 实例
#include <future>
#include <thread>
#include <iostream>
void asyncFunc(){
std::cout << "do right now in a new thread...\n";
}
void deferFunc(){
std::cout << "doing refered...\n";
}
int main()
{
auto fut1 = std::async(std::launch::async, asyncFunc);
auto fut2 = std::async(std::launch::async, deferFunc);
std::cout << "before fut1 get.\n";
fut2.get();
std::cout << "after fut2 get.\n";
fut1.get();
}
// do right now in a new thread...
// before fut1 get.
// doing refered...
// after fut2 get.
上面可以看到,使用std::launch::async
时,函数立刻就开始执行了,而使用std::launch::async
是到使用std::future.get
方法时,函数才执行,起到了延时执行的效果。
当使用std::launch::async
时,会创建新的线程,使用ps -T -p <PID>
可以查看进程下启动的线程。
ps -T -p 363107
# PID SPID TTY TIME CMD
# 363107 363107 pts/2 00:00:00 af
# 363107 363108 pts/2 00:00:00 af
SPID
行表示的是Stack Pointer ID
进程下的栈指针ID
,也就是linux
内核意义上的进程。
# std::promise
# std::promise介绍
std::promise
是C++11
中引人的模板类,非空特化可以用来在线程之间交换对象,void
特化可以用来在调度状态无关的事件。
std::promise
用于异步的存储一个值或异常,并通过返回一个std::future
对象来获取对应的值。它允许一个线程(通常是生产者线程)承诺将某个值或异常传递给另一个线程(通常是消费者线程)。
# std::promise实例
#include <future>
#include <thread>
#include <iostream>
#include <vector>
#include <numeric>
void accAdd(std::vector<int>::iterator begin,
std::vector<int>::iterator end,
std::promise<int> promise)
{
int sum = std::accumulate(begin, end, 0);
promise.set_value(sum);
}
void voidFunc(std::promise<void> promise) {
std::this_thread::sleep_for(std::chrono::seconds(2));
promise.set_value();
}
int main()
{
std::vector<int> nums{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
std::promise<int> accumulated_promise;
std::future<int> accumulated_future = accumulated_promise.get_future();
std::thread t1(accAdd, nums.begin(), nums.end(), std::move(accumulated_promise));
t1.join();
std::cout << "accumulated_future: " << accumulated_future.get() << std::endl;
std::promise<void> barrier_promise;
std::future<void> barrier_future = barrier_promise.get_future();
std::thread t2(voidFunc, std::move(barrier_promise));
std::cout << "barrier thread is waiting set_value event...\n";
barrier_future.wait();
std::cout << "barrier thread waiting job finished.\n";
t2.join();
}
上面的代码可以看到std::promise
的使用方式,先定一个std::promise
对象,然后通过移动语义将其传入到线程中,在线程的任务函数中调用其set_value
方法赋值,结合std::future
来阻塞获取promise
的返回值。
# std::packaged_task
# std::packaged_task介绍
std::packaged_task
也是一个类模板,模板特化使用的参数是函数的签名,接受可调用参数(函数/lambda表达式/bind表达式或函数对象)作为参数以把可调用函数转换成异步任务。其函数返回值和抛出的异常保存在一个共享状态中,通过std::future
对象来获取共享状态的值。
# std::packaged_task实例
#include <future>
#include <thread>
#include <iostream>
#include <vector>
#include <numeric>
#include <functional>
void task_lambda()
{
std::packaged_task<int(int, int)> task([](int a, int b){
return a + b;
});
std::future<int> task_future = task.get_future();
task(11, 23);
std::cout << "task_lambda:\t" << task_future.get() << '\n';
}
void task_bind()
{
auto f = std::bind(pkg_task_add, 13, 15);
std::packaged_task<int()> task(f);
std::future<int> task_future = task.get_future();
task();
std::cout << "task_bind:\t" << task_future.get() << '\n';
}
void task_thread()
{
std::packaged_task<int(int, int)> task(pkg_task_add);
std::future<int> task_future = task.get_future();
std::thread task_td(std::move(task), 2, 10);
task_td.join();
std::cout << "task_thread done, result is ready. waiting for waking up...\n";
std::this_thread::sleep_for(std::chrono::seconds(10));
std::cout << "task_thread:\t" << task_future.get() << '\n';
}
int main()
{
task_lambda();
task_bind();
task_thread();
}
使用std::packaged_task
对函数进行封装后,可以借用std::future
对象将值的获取和函数的运行分离,在先调用函数,然后调用关联future
对象的get
方法获取函数的运行结果。
# reference
1.https://en.cppreference.com/w/cpp/thread/async (opens new window)
2.https://en.cppreference.com/w/cpp/thread/promise (opens new window)
3.https://en.cppreference.com/w/cpp/thread/packaged_task (opens new window)