c++11新特性之future-PROMISE
std::future可以从异步任务中获取结果,一般与std::async配合使用,std::async用于创建异步任务,实际上就是创建一个线程执行相应任务。
先看段代码:
#include <future>
#include <iostream>
#include <stout/stringify.hpp>
bool is_prime(int x)
{
for (int i=0; i<x; i++)
{
if (x % i == 0)
return false;
}
return true;
}
int main()
{
std::future<bool> fut = std::async(is_prime, 700020007);
std::cout << "please wait";
std::chrono::milliseconds span(100);
while (fut.wait_for(span) != std::future_status::ready)
std::cout << ".";
std::cout << std::endl;
bool ret = fut.get();
std::cout << "final result: " << stringify(ret) << std::endl;
return 0;
}
std::async会首先创建线程执行is_prime(700020007), 任务创建之后,std::async立即返回一个std::future对象。
主线程既可使用std::future::get获取结果,如果调用过程中,任务尚未完成,则主线程阻塞至任务完成。
主线程也可使用std::future::wait_for等待结果返回,wait_for可设置超时时间,如果在超时时间之内任务完成,则返回std::future_status::ready状态;如果在超时时间之内任务尚未完成,则返回std::future_status::timeout状态。
===========================================
C++11 多线程 future/promise简介
原创jiange_zh 最后发布于2016-06-07 13:29:27 阅读数 16420 收藏
展开
1. < future >头文件简介
Classes
std::future
std::future_error
std::packaged_task
std::promise
std::shared_future
Functions
std::async
std::future_category
2. std::future
简单来说,std::future提供了一种访问异步操作结果的机制。
从字面意思看,它表示未来。通常一个异步操作我们是不能马上就获取操作结果的,只能在未来某个时候获取。我们可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作的结果。future_status有三种状态:
deferred:异步操作还没开始
ready:异步操作已经完成
timeout:异步操作超时
获取future结果有三种方式:get、wait、wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。
例子:
//查询future的状态
std::future_status status;
do {
status = future.wait_for(std::chrono::seconds(1));
if (status == std::future_status::deferred) {
std::cout << "deferred\n";
} else if (status == std::future_status::timeout) {
std::cout << "timeout\n";
} else if (status == std::future_status::ready) {
std::cout << "ready!\n";
}
} while (status != std::future_status::ready);
3. std::promise
Promise对象可保存T类型的值,该值可被future对象读取(可能在另一个线程中),这是promise提供同步的一种手段。在构造promise时,promise对象可以与共享状态关联起来,这个共享状态可以存储一个T类型或者一个由std::exception派生出的类的值,并可以通过get_future来获取与promise对象关联的对象,调用该函数之后,两个对象共享相同的共享状态(shared state)。
Promise对象是异步provider,它可以在某一时刻设置共享状态的值。
Future对象可以返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标识变为ready,然后才能获取共享状态的值。
例子:
#include <iostream> // std::cout
#include <functional> // std::ref
#include <thread> // std::thread
#include <future> // std::promise, std::future
void print_int(std::future<int>& fut) {
int x = fut.get(); // 获取共享状态的值.
std::cout << "value: " << x << '\n'; // 打印 value: 10.
}
int main ()
{
std::promise<int> prom; // 生成一个 std::promise<int> 对象.
std::future<int> fut = prom.get_future(); // 和 future 关联.
std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t.
prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步.
t.join();
return 0;
}
std::promise 构造函数
构造函数
default (1) promise();
with allocator (2) template promise (allocator_arg_t aa, const Alloc& alloc);
copy [deleted] (3) promise (const promise&) = delete;
move (4) promise (promise&& x) noexcept;
1.默认构造函数,初始化一个空的共享状态。
2.带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。
3.拷贝构造函数,被禁用。
4.移动构造函数。
另外,std::promise 的 operator= 没有拷贝语义,即 std::promise 普通的赋值操作被禁用,operator= 只有 move 语义,所以 std::promise 对象是禁止拷贝的。
std::promise 成员函数
std::promise::get_future:返回一个与promise共享状态相关联的future对象
std::promise::set_value:设置共享状态的值,此后promise共享状态标识变为ready
std::promise::set_exception:为promise设置异常,此后promise的共享状态标识变为ready
std::promise::set_value_at_thread_exit:设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready(注意:该线程已设置promise的值,如果在线程结束之后有其他修改共享状态值的操作,会抛出future_error(promise_already_satisfied)异常)
std::promise::swap:交换 promise 的共享状态
4. std::packaged_task
std::packaged_task包装了一个可调用的目标(如function, lambda expression, bind expression, or another function object),以便异步调用,它和promise在某种程度上有点像,promise保存了一个共享状态的值,而packaged_task保存的是一个函数。
std::packaged_task<int()> task([](){ return 7; });
std::thread t1(std::ref(task));
std::future<int> f1 = task.get_future();
auto r1 = f1.get();
5. 小结
Promise,Future 和 Callback常常作为并发编程中一组非阻塞的模型。其中 Future 表示一个可能还没有实际完成的异步任务的【结果】,针对这个结果可以添加 Callback 以便在任务执行成功或失败后做出对应的操作,而 Promise 交由任务执行者,任务执行者通过 Promise 可以标记任务完成或者失败。
6. std::async
std::async大概的工作过程:先将异步操作用std::packaged_task包装起来,然后将异步操作的结果放到std::promise中,这个过程就是创造未来的过程。外面再通过future.get/wait来获取这个未来的结果。
可以说,std::async帮我们将std::future、std::promise和std::packaged_task三者结合了起来。
std::async的原型:
async(std::launch::async | std::launch::deferred, f, args...)
1
第一个参数是线程的创建策略,默认的策略是立即创建线程:
std::launch::async:在调用async就开始创建线程。
std::launch::deferred:延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait时才创建线程。
第二个参数是线程函数,后面的参数是线程函数的参数。
简单的例子:
std::future<int> f1 = std::async(std::launch::async, [](){
return 8;
});
cout<<f1.get()<<endl; //output: 8
std::future<int> f2 = std::async(std::launch::async, [](){
cout<<8<<endl;
});
f2.wait(); //output: 8
==========================================================================================================================================================================================================================
std::packaged_task<>会将future与函数或可调用对象进行绑定。当调用std::packaged_task<>对象时,就会调用相关函数或可调用对象,当future状态为就绪时,会存储返回值。这可以用在构建线程池或其他任务的管理中,比如:在任务所在线程上运行其他任务,或将它们串行运行在一个特殊的后台线程上。当粒度较大的操作被分解为独立的子任务时,每个子任务都可以包含在std::packaged_task<>实例中,之后将实例传递到任务调度器或线程池中。对任务细节进行抽象,调度器仅处理std::packaged_task<>实例,而非处理单独的函数。
888888888888888888888888888888888888888888
777777777777777777777777777777777777777777
66666666666666666666666666666666666666666666
std::packaged_task<int()> task(sleep);
? ??
auto f = task.get_future();
555555555555555555555555555555555555555555555
666666666666666666666666666666666666666666666
777777777777777777777777777777777777777777777
provider?? ??? ??? ?std::promise?? ??? ??? ?std::packaged_task
futures?? ??? ??? ??? ?std::futures?? ??? ??? ?std::shared_future
aaaaaaaaaaaaaaaaaaa
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
cccccccccccccccccccccccccccccccccccccccccc
/********************************
简单来说,std::future提供了一种访问异步操作结果的机制。
从字面意思看,它表示未来。通常一个异步操作我们是不能马上就获取操作结果的,只能在未来某个时候获取。我们可以以同步等待的方式来获取结果,可以通过查询future的状态(future_status)来获取异步操作的结果。future_status有三种状态:
deferred:异步操作还没开始
ready:异步操作已经完成
timeout:异步操作超时
获取future结果有三种方式:get、wait、wait_for,其中get等待异步操作结束并返回结果,wait只是等待异步操作完成,没有返回值,wait_for是超时等待返回结果。
**********************************/
#include <iostream> ? ? ? // std::cout
#include <thread> ? ? ? ? // std::thread
#include <future> ? ? ? ? // std::promise, std::future
int main ()
{
? ? // constructor/get_future/operator=/valid
?? ?std::packaged_task<int(int)> task1; // default-constructed
?? ?std::packaged_task<int(int)> task2([](int x) { return x * 2; }); // initialized
?
?? ?task1 = std::move(task2); // move-assignment
?? ?std::cout << "valid: " << task1.valid() << "\n";
?? ?std::future<int> ret = task1.get_future(); // get future
?? ?std::thread(std::move(task1), 10).detach(); // spawn thread and call task派生线程和调用任务
?
?? ?int value = ret.get(); // wait for the task to finish and get result
?? ?std::cout << "The double of 10 is " << value << ".\n";
}
复制代码
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
C++11之std::future和std::promise
为什么C++11引入std::future和std::promise?C++11创建了线程以后,我们不能直接从thread.join()得到结果,必须定义一个变量,在线程执行时,对这个变量赋值,然后执行join(),过程相对繁琐。
thread库提供了future用来访问异步操作的结果。std::promise用来包装一个值将数据和future绑定起来,为获取线程函数中的某个值提供便利,取值是间接通过promise内部提供的future来获取的,也就是说promise的层次比future高。?
#include "stdafx.h" #include <iostream> #include <type_traits> #include <future> #include <thread> using namespace std; int main() { std::promise<int> promiseParam; std::thread t([](std::promise<int>& p) { std::this_thread::sleep_for(std::chrono::seconds(10));// 线程睡眠10s p.set_value_at_thread_exit(4);// }, std::ref(promiseParam)); std::future<int> futureParam = promiseParam.get_future(); auto r = futureParam.get();// 线程外阻塞等待 std::cout << r << std::endl; return 0; }
上述程序执行到futureParam.get()时,有两个线程,新开的线程正在睡眠10s,而主线程正在等待新开线程的退出值,这个操作是阻塞的,也就是说std::future和std::promise某种程度也可以做为线程同步来使用。
std::packaged_task包装一个可调用对象的包装类(如function,lambda表达式(C++11之lambda表达式),将函数与future绑定起来。std::packaged_task与std::promise都有get_future()接口,但是std::packaged_task包装的是一个异步操作,而std::promise包装的是一个值。
#include "stdafx.h" #include <iostream> #include <type_traits> #include <future> #include <thread> using namespace std; int main() { std::packaged_task<int()> task([]() { std::this_thread::sleep_for(std::chrono::seconds(10));// 线程睡眠10s return 4; }); std::thread t1(std::ref(task)); std::future<int> f1 = task.get_future(); auto r = f1.get();// 线程外阻塞等待 std::cout << r << std::endl; return 0; }
而std::async比std::promise, std::packaged_task和std::thread更高一层,它可以直接用来创建异步的task,异步任务返回的结果也保存在future中。std::async的原型:
async( std::launch policy, Function&& f, Args&&... args );
std::launch policy有两个,一个是调用即创建线程(std::launch::async),一个是延迟加载方式创建线程(std::launch::deferred),当掉使用async时不创建线程,知道调用了future的get或者wait时才创建线程。之后是线程函数和线程参数。
#include "stdafx.h" #include <iostream> #include <future> #include <thread> int main() { // future from a packaged_task std::packaged_task<int()> task([]() { std::cout << "packaged_task started" << std::endl; return 7; }); // wrap the function std::future<int> f1 = task.get_future(); // get a future std::thread(std::move(task)).detach(); // launch on a thread // future from an async() std::future<int> f2 = std::async(std::launch::deferred, []() { std::cout << "Async task started" << std::endl; return 8; }); // future from a promise std::promise<int> p; std::future<int> f3 = p.get_future(); std::thread([&p] { p.set_value_at_thread_exit(9); }).detach(); f1.wait(); f2.wait(); f3.wait(); std::cout << "Done!nResults are: " << f1.get() << ' ' << f2.get() << ' ' << f3.get() << 'n'; }
zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
YYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
C++11 异步操作
目录
异步操作简介
什么是异步操作,为何会有异步操作?
在C++中,不能直接从thread.join()得到结果,必须定义个变量,在线程执行时,对这个变量赋值,然后执行join(),过程相对繁琐。
Linux中有AIO(异步IO)做异步操作,C++中如何进行异步操作?
答:是有的,C++11提供了异步操作相关的类,主要有std::future, std::promise, std::package_task。
Linux AIO参见Linux 异步IO(AIO)
- std::future 作为异步结果传输通道,可以方便获取线程函数的返回值;
- std::promise 用来包装一个值,将数据和future绑定起来,方便线程赋值;
- std::package_task 用来包装一个可调用对象,将函数和future绑定起来,便于异步调用;
头文件:
std::future
获取线程返回值的类std::future,用来访问异步操作的结果。因为异步操作需要时间,future并不能马上获取其结果,只能在未来某个时候获取期待值,因此被称为future。
可以通过同步等待的方式,通过查询future的状态(future_status),来获取异步操作的结果。
future状态有3种:
1)Deferred 异步操作还没开始;
2)Ready 异步操作已经完成;
3)Timeout 异步操作超时;
获取future异步操作的结果,有4种方式:
1)get 等待异步结束并返回结果;
2)wait 只是等待异步操作结束,没有返回值;
3)wait_for 超时等待异步操作返回结果;
4)wait_until 等待达到指定的时间点,异步操作返回结果,常配合当前时间点 + 时间段来设置目标时间点;
其同步调用基本用法为:
// 同步查询future状态的例子
// 异步求和,同步查询
int work0(int a, int b) {
this_thread::sleep_for(chrono::seconds(4)); // 通过休眠,模拟异步操作需要一定时间完成
return a + b;
}
std::future_status status;
std::future<int> future;
future = async(work0, 12, 34); // 注意future经常搭配std::async使用,async参见下文
/* 等待、轮询future状态 */
do {
status = future.wait_for(chrono::seconds(1)); // 定时获取future的状态
if (status == future_status::deferred) { // 异步操作还没开始
cout << "deferred" << endl;
}
else if (status == future_status::timeout) { // 异步操作超时
cout << "timeout" << endl;
}
else if (status == future_status::ready) { // 异步操作已完成
cout << "ready" << endl;
}
} while (status != future_status::ready);
std::promise
协助线程赋值的类std::promise,将数据和future绑定。在线程函数中,为外面传入的promise对象赋值,线程函数执行完毕后,就可以通过promise的future获取该值。
注意:future是定义在promise内部的成员。
get_future函数
函数返回一个与promise共享状态相关联的future对象。返回的future对象可以访问由promise对象设置在共享状态上的值或者某个异常对象。
promise对象通常会在某个时间点准备好(设置一个值或者一个异常对象),然后在另一个线程中,用future对象的get获取值。
future<_Ty> get_future()
set_value函数
设置共享状态值,此后promise的共享状态标志变为ready
void set_value(const _Ty& _Val)
set_exception函数
为promise设置异常,此后promise的共享状态标志变为ready
void set_exception(_XSTD exception_ptr _Exc)
promise基本用法
promise<int> pr;
future<int> f = pr.get_future();
thread t([](promise<int> &p) { p.set_value_at_thread_exit(9); }, ref(pr)); // lambda是匿名函数,ref(pr)是该函数实参
t.detach();
try {
auto r = f.get();
cout << "get future from promise: " << r << endl;
}
catch (const exception& e) {
cout << "exception: " << e.what() << endl;
}
可以将上面线程函数由lambda改成使用普通的线程函数:
thread t([](promise<int> &p) { p.set_value_at_thread_exit(9); }, ref(pr));
// <=>
thread t(work1, ref(pr));
void work1(promise<int> &pr) {
this_thread::sleep_for(chrono::seconds(3));
#if 0
try {
throw runtime_error("Runtime error");
}
catch (...) {
pr.set_exception(current_exception());
}
#else
pr.set_value(9);
#endif
}
promise有set_value和set_value_at_thread_exit,都可以用来对promise赋值,区别在于后者要求赋值的promise对象不能被销毁(如不能使用右值传值,然后在内部用std::move构造临时promise对象),而前者没有这个要求。
参见关于std::promise的set_value_at_thread_exit | CSDN
std::package_task
可调用对象的包装类std::package_task,包装了一个可调用的目标类(如function, lambda expression, bind expression, another function object),将其与future绑定,以便异步调用。
promise和package_task类似,promise保存了一个共享状态的值,package_task保存的是一个可调用对象(如函数)。
package_task基本用法
packaged_task<int()> task([]() {return 7; }); // 包装可调用对象(lambda expression)
thread t2(ref(task));
future<int> f1 = task.get_future();
auto r1 = f1.get();
t2.detach();
cout << r1 << endl;
std::promise, std::package_task, std::future三者关系
future提供异步操作结果访问机制,跟线程一个级别,属于低层次对象。
promise和package_task是在future的高一层,内部封装了future以便访问异步操作结果。promise内部包装的是一个值,package_task内部包装的是一个可调用对象。当需要线程中的某个值时,使用promise;当需要获得一个异步操作的返回值时,使用package_task。
简单来说,package_task会自动将异步操作的结果保存到promise对象中,而promise需要在线程函数中手动设置promise值。
另外,可以用future保存异步操作结果,但future的copy函数是禁用的,只能使用move函数。而shared_future是可以copy的。因此,当要把future保存到容器中时,请使用shared_future。
package_task和shared_future基本用法
int func(int x) {
return x + 2;
}
packaged_task<int(int)> task(func);
future<int> ft = task.get_future();
thread(std::move(task), 2).detach(); // task作为线程函数
int res = ft.get();
cout << res << endl; // 打印4
// future不可copy,无法放入容器。要放入容器,使用shared_future
vector<shared_future<int>> vec;
auto f = std::async(launch::async, [](int a, int b) { return a + b; }, 2, 3).share(); // 注意这里的share()将future转化为shared_future
vec.push_back(f);
cout << "The shared_future result is " << vec[0].get() << endl; // 打印5
std::async
线程异步操作函数std::async,比promise, package_task, thread更高一层,可用来创建异步task,其返回结果保存到future对象中。当需要取得异步结果时,调用future.get()即可。如果不关注结果,只是简单等任务完成,可以使用future.wait()
async原型:
async(std::launch::async | std::launch::deferred, f, args,...);
-
第一个参数是线程的创建策略,默认是std::launch::async。
1)std::launch::async 调用async时就开始创建线程;
2)std::launch::deferred 延迟加载方式创建线程。调用async时不创建线程,直到调用了future的get或者wait,才创建线程。 -
第二个参数是线程函数
-
第三个参数是传入线程函数的参数
async使用示例
// 使用launch::async策略创建线程,并阻塞get其值
future<int> f1 = async(launch::async, []() {return 8; });
cout << f1.get() << endl; // 打印8
// 使用launch::async策略创建线程,并轮询future状态
future<int> f2 = async(launch::async, []() {
this_thread::sleep_for(chrono::seconds(3));
return 8; });
cout << "waiting..." << endl;
future_status status;
do {
status = f2.wait_for(chrono::seconds(1));
if (status == future_status::deferred) { // 异步操作尚未完成
cout << "deferred" << endl;
}
else if (status == future_status::timeout) { // 异步操作超时
cout << "timeout" << endl;
}
else if (status == future_status::ready) {
cout << "ready!" << endl;
}
} while (status != future_status::ready);
cout << "result is " << f2.get() << endl;
// 使用launch::deferred策略创建线程,并轮询future状态
future<int> f3 = async(launch::deferred, []() { return 3; });
future_status status3;
do {
status3 = f3.wait_for(chrono::seconds(1));
if (status3 == future_status::deferred) {
cout << "deferred" << endl;
}
} while (status3 != future_status::ready);
CPP 折叠 复制 全屏
参考
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!