上一篇:《深入应用C++11》笔记-互斥量std::mutex、锁std::lock_guard
std::async和std::future
std::async()是一个接受回调函数或函数对象作为参数的函数模板,并可以异步执行它们。通过这个异步接口可以很方便的获取线程函数的执行结果,std::async会自动创建一个线程去调用线程函数,它返回一个std::future。
std::future中存储了线程函数返回的结果,当我们需要线程函数的结果时,直接从future中获取。std::async首先解耦了线程的创建和执行,使得我们可以在需要的时候获取异步操作的结果;其次它还提供了线程的创建策略(比如可以通过延迟加载的方式去创建线程),可以以多种方式去创建线程。
template<class Fn, class... Args>
future<typename result_of<Fn(Args...)>::type> async(launch policy, Fn&& fn, Args&&...args);
std::async中的第一个参数是启动策略,它控制std::async的异步行为,共有三类:
- std::launch::async:立即开始执行线程
- std::launch::deferred:调用get()函数时才开始执行线程
- std::launch::async | std::launch::deferred:默认行为。有了这个启动策略,它可以异步运行或不运行,这取决于系统的负载,但我们无法控制它。
#include <iostream>
#include <future>
#include <chrono>
int func()
{
std::cout << "run async" << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100));
return 1;
}
int main()
{
auto handle = std::async(std::launch::async, func);
// 用deferred输出结果顺序将会不同
//auto handle = std::async(std::launch::deferred, func);
std::this_thread::sleep_for(std::chrono::milliseconds(100));
std::cout << "do something" << std::endl;
std::cout << handle.get() << std::endl;
return 0;
}
输出结果:
run async
do something
1
可以看到,std::async异步执行了func函数,并且能够通过handle获取到func函数的返回值。
返回值handle实际上是std::future模板类对象,可使用std::future::get获取结果,如果调用过程中,任务尚未完成,则主线程阻塞至任务完成。也可使用std::future::wait_for等待结果返回,wait_for可设置超时时间,如果在超时时间之内任务完成,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态。更加详细的用法参考:https://blog.csdn.net/watson2016/article/details/52860797
std::promise
promise 对象可以保存某一类型 T 的值,该值可被 future 对象读取。可以通过 get_future 来获取与该 promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state)。
#include <iostream>
#include <functional>
#include <thread>
#include <future>
void func(std::promise<int>& pro){
std::this_thread::sleep_for(std::chrono::milliseconds(2000));
pro.set_value(10); // 设置值
}
int main()
{
std::promise<int> pro;
std::thread t(func, std::ref(pro));
std::future<int> fut = pro.get_future();
std::cout << fut.get() << std::endl; // 10,调用get函数等待pro设置值
t.join();
return 0;
}
另外std::promise还提供了set_exception函数,让future对象的get函数抛出异常;提供set_value_at_thread_exit用于在线程退出时设置值,也就是说直到线程退出get阻塞才会结束,并且std::promise对象不能在线程结束前被释放。
std::packaged_task
std::packaged_task 和 std::promise很类似,不过他包装一个可调用的对象而不是类型对象,并且允许异步获取该可调用对象产生的结果。
int func(int from, int to) {
std::this_thread::sleep_for(std::chrono::seconds(2));
return from - to;
}
int main()
{
std::packaged_task<int(int, int)> task(func);
std::future<int> ret = task.get_future();
std::thread th(std::move(task), 10, 5);
std::cout << ret.get() << std::endl; // 5,调用get函数等待func的返回值
th.join();
return 0;
}
valid函数,判断是否有效,对于由默认构造函数生成的 packaged_task 对象,该函数返回 false:
std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg)
{
if (tsk.valid()) { // 无效返回false
std::future<int> ret = tsk.get_future();
std::thread(std::move(tsk), arg).detach();
return ret;
}
else return std::future<int>();
}
int main()
{
std::packaged_task<int(int)> tsk; // 没有给packaged_task初始化
std::future<int> fut = launcher(tsk, 25);
if (fut.valid()) // 无效返回false
{
std::cout << << fut.get() << std::endl;
}
return 0;
}
std::packaged_task还提供了reset函数,用于重置std::packaged_task状态,让他能够再次调用;提供make_ready_at_thread_exit函数,直到线程退出get阻塞才会结束,而不是可调用对象结束时。