目录
future介绍
future 是一个用来获取异步任务的结果,其存在的意义
其实就是为了解决 std::thread 无法返回值的问题
future可以想象成一个对未来的预言,定义它的时候,相当于某个预言家预言在未来的某一天会出现这个事件。
- 预言只有一个,但预言家们倒是有很多个(即获得future的方式)
1.std::async 函数会返回一个std::future
2.std::promise::get_future 调用成员函数,获取 std::future
3.std::packaged_task::get_future 调用成员函数,获取 std::future
std::future对象只有在有效的(valid)情况下才有用(useful)
- 既然是一个预言,那肯定要有内容,说明这个事件出现后会带来什么
如果不说明白,只告诉一个名称,那就只有靠猜了,就没什么意义
所以std::future 默认构造函数创建的 future 对象不是有效的,没有什么意义。
但如果把这个名称和其他有意义的故事连在一起,那就有意义了
比如有一个叫Tom的人会协助英雄拯救世界,那Tom这个名称就有价值了
即用一个有效的 future 对象 move 赋值给 当前非有效的 future 对象 )
// 由默认构造函数创建的 std::future 对象,
// 初始化时该 std::future 对象处于为 invalid 状态.
std::future<int> foo, bar;
foo = std::async(do_get_value); // move 赋值, foo 变为 valid.
bar = std::move(foo); // move 赋值, bar 变为 valid, 而 move 赋值以后 foo 变为 invalid.
查询future对象是否有效
if (forecast.valid())
std::cout << "forecast's value: " << forecast.get() << '\n';
else
std::cout << "forecast is not valid\n";
future的拷贝构造函数是被禁用的
std::future 的拷贝构造函数是被禁用的,只提供了默认的构造函数和 move 构造函数(注:C++ 新特新)。另外,std::future 的普通赋值操作也被禁用,只提供了 move 赋值操作。
预言这种东西,是没有办法复制的。
对future的处理
比如预言家说,世界要毁灭了,世界上可能就会有四种不同态度的人
四种方式
询问获取future结果有四种方式:get、wait、wait_until、wait_for
- get 等待异步操作结束并返回结果
- wait 只是等待异步操作完成,没有返回值
- wait_until 与wait类似,但可以设置一个绝对时间点
- wait_for 是超时等待返回结果
一个使用了wait()和get()的例子
#include <iostream> // std::cout
#include <future> // std::async, std::future
#include <chrono> // std::chrono::milliseconds
// a non-optimized way of checking for prime numbers:
bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化.
{
for (int i = 2; i < x; ++i)
if (x % i == 0)
return false;
return true;
}
int main()
{
// call function asynchronously:
std::future < bool > fut = std::async(do_check_prime, 194232491);
std::cout << "Checking...\n";
fut.wait();
std::cout << "\n194232491 ";
if (fut.get()) // guaranteed to be ready (and not block) after wait returns
std::cout << "is prime.\n";
else
std::cout << "is not prime.\n";
return 0;
}
- get代表的应该是比较乐观的一类,等待预言到来,好好观赏那最后的一天。
(等待线程结束,获取返回值) - wait 代表的比较悲观,什么预言都不管了,最后那天什么样都没兴趣。
(等待线程结束,没有返回值) - wait_until 比较奇怪,一开始都和wait()一样,但不同的给了自己一个时间,到了那个时间,就变成wait_for 去问一下预言家发生了吗,并返回状态。
(在指定时间内等待,时间到了,如果共享状态还不ready,就返回状态) - wait_for 就是比较正常的人,跑去问预言家预言还有多久会发生
(获取future的状态)
// wait_until 使用
#include <iostream>
#include <future>
#include <thread>
#include <chrono>
int main()
{
std::chrono::system_clock::time_point two_seconds_passed
= std::chrono::system_clock::now() + std::chrono::seconds(2);
// 做出花 1 秒完成的 future
std::promise<int> p1;
std::future<int> f_completes = p1.get_future();
std::thread([](std::promise<int> p1)
{
std::this_thread::sleep_for(std::chrono::seconds(1));
p1.set_value_at_thread_exit(9);
},
std::move(p1)
).detach();
// 做出花 5 秒完成的 future
std::promise<int> p2;
std::future<int> f_times_out = p2.get_future();
std::thread([](std::promise<int> p2)
{
std::this_thread::sleep_for(std::chrono::seconds(5));
p2.set_value_at_thread_exit(8);
},
std::move(p2)
).detach();
std::cout << "Waiting for 2 seconds..." << std::endl;
if(std::future_status::ready == f_completes.wait_until(two_seconds_passed))
{ std::cout << "f_completes: " << f_completes.get() << "\n"; }
else
{ std::cout << "f_completes did not complete!\n"; }
if(std::future_status::ready == f_times_out.wait_until(two_seconds_passed))
{ std::cout << "f_times_out: " << f_times_out.get() << "\n"; }
else
{ std::cout << "f_times_out did not complete!\n"; }
std::cout << "Done!\n";
}
/*
输出
Waiting for 2 seconds...
f_completes: 9
f_times_out did not complete!
Done!
*/
但只要预言成真了,std::future 对象相关联的共享状态的标志就会变为 ready
获取future的状态
就要查询future的状态,这里要使用的是wait_for
future_status的三种状态:
- deferred:异步操作还没开始(还没到时间)
- ready:异步操作已经完成(就是现在)
- timeout:异步操作超时(预言家是个骗子)
但在一个有效的future对象上调用get会阻塞当前的调用者,
直到 Provider 设置了共享状态的值或异常(此时共享状态的标志变为 ready)
下面就是一个不断循环询问状态的过程
std::future_status status;
do
{
// 我们对预言的实现的时限忍耐是很有限的,我们只能忍一秒
status = fu.wait_for(std::chrono::seconds(1));
if(status == std::future_status::deferred) //异步操作还没开始
std::cout << "deferred" << std::endl; // 那还可以等等
else if(status == std::future_status::timeout) //异步操作超时
std::cout << "timeout" << std::endl; // 抡起大棒打骗子
else if(status == std::future_status::ready) //异步操作已完成
std::cout << "ready" << std::endl; // 真的发生了!
}while(status != std::future_status::ready);
shared_future介绍
shared_future 提供了一种访问异步操作结果的机制,
允许多个线程等待同一个共享状态,既支持移动操作也支持拷贝操作,
可以引用相同的共享状态,允许一旦共享状态就绪就可以多次检索共享状态下的值
future是一个只能讲给一个人听的秘密预言,而且阅后即毁。
所以future只能调用一次get,如果已经get过,其共享状态就会被释放,再次使用就会报错。
但如果想告诉更多的人都听到这个预言,就要用到shared_future
shared_future 在get后,不会释放共享状态,可以拷贝,共享某个共享状态的最终结果
future转换为shared_future
shared_future 可以通过 future 对象隐式转换
也可以通过显式调用std::future::share显式转换
注意:当future转换为shared_future后,原future对象就会变的无效。
// 隐式转换
// 将std::shared_future<int>调整为std::future<int>也是正确的
std::shared_future<int> f1 = std::async(std::launch::async, []() { return fib(20); });
// 显式转换
// 通过std::future移动构造std::shared_future对象
std::promise<void> ready_promise;
std::shared_future<void> ready_future(ready_promise.get_future());
// 从 promise 获取 future,并赋值给shared_future
promise<int> prom;
shared_future<int> sf1 = std::move(prom.get_future());
// 调用std::future::share显式转换
std::future<int> fut = std::async(do_get_value);
std::shared_future<int> shared_fut = fut.share();
对shared_future进行处理
和future是一样的,还是只有四种方式get,wait,wait_for,wait_until
shared_future和future的差别就体现在shared 分享,其他都是一样的
future_error
future error类定义了一个异常对象,
当线程库中处理异步执行和共享状态的函数失败时抛出该异常对象
你可能会见过这样一个错误
terminate called after throwing an instance of 'std::future_error'
what(): std::future_error: Promise already satisfied
举一些会报错的例子就准备结束了,下面这些例子来自这篇好文
同时加了一些自己的注释
// 当future状态不是ready时抛错
#include <future>
#include <iostream>
int main()
{
std::future<int> empty;
try {
// 只做出了预言,但预言的内容没有说出来
int n = empty.get(); // The behavior is undefined, but
// some implementations throw std::future_error
} catch (const std::future_error& e) {
std::cout << "Caught a future_error with code \"" << e.code()
<< "\"\nMessage: \"" << e.what() << "\"\n";
}
}
// 两次调用 set_value 这个可以看我另一篇博文《通俗讲解c++ promise》
void test(std::promise<int>& para){
para.set_value(10);
para.set_value(20);
return;
}
int main(){
std::promise<int> pro;
std::future<int> T = pro.get_future();
std::thread fun(test, std::ref(pro));
fun.join();
cout << T.get() << endl;
return 0;
}
检测future是否是引用共享状态
这个例子只发生在 future 第一次调用get() 或者 share() 之前,
没有调用"默认构造函数"或者"移动构造函数"
预言如果没有内容,就无法用任何语言对它进行描述
除了析构、赋值、valid函数之外,在没有指向共享状态的future中,调用其他任何成员函数,该行为都被定义为无效行为(在实际使用中鼓励抛出异常,从而表明当前状态不满足)
//
void test(std::promise<int>& para){
para.set_value(10);
//para.set_value(20);
return;
}
int main(){
std::promise<int> pro;
std::future<int> T = pro.get_future();
std::thread fun(test, std::ref(pro));
fun.join();
cout << T.get() << endl;
// 在调用get()后,共享状态就已经解除了,这时的get_future就是无效行为
auto Error_T = pro.get_future();
return 0;
}
/* 输出
10
terminate called after throwing an instance of 'std::future_error'
what(): std::future_error: Future already retrieved
[1] 14518 abort ./a.out
*/
参考
- https://www.cnblogs.com/haippy/p/3280643.html
- https://blog.csdn.net/weixin_36953500/article/details/90342603?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.edu_weight&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.edu_weight
- https://blog.csdn.net/fengbingchun/article/details/104118831
- https://www.yar2001.com/cpp/reference/zh/cpp/thread/future/wait_until.html
- https://blog.csdn.net/weixin_43705457/article/details/104066817
如有错误,还请指正