future 是一个用来获取异步任务的结果,其存在的意义
其实就是为了解决 std::thread 无法返回值的问题
future可以想象成一个对未来的预言,定义它的时候,相当于某个预言家预言在未来的某一天会出现这个事件。
所以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.
if (forecast.valid())
std::cout << "forecast's value: " << forecast.get() << '\n';
else
std::cout << "forecast is not valid\n";
std::future 的拷贝构造函数是被禁用的,只提供了默认的构造函数和 move 构造函数(注:C++ 新特新)。另外,std::future 的普通赋值操作也被禁用,只提供了 move 赋值操作。
预言这种东西,是没有办法复制的。
比如预言家说,世界要毁灭了,世界上可能就会有四种不同态度的人
询问获取future结果有四种方式:get、wait、wait_until、wait_for
一个使用了wait()和get()的例子
#include // std::cout
#include // std::async, std::future
#include // 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;
}
// wait_until 使用
#include
#include
#include
#include
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的状态,这里要使用的是wait_for
但在一个有效的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 提供了一种访问异步操作结果的机制,
允许多个线程等待同一个共享状态,既支持移动操作也支持拷贝操作,
可以引用相同的共享状态,允许一旦共享状态就绪就可以多次检索共享状态下的值
future是一个只能讲给一个人听的秘密预言,而且阅后即毁。
所以future只能调用一次get,如果已经get过,其共享状态就会被释放,再次使用就会报错。
但如果想告诉更多的人都听到这个预言,就要用到shared_future
shared_future 在get后,不会释放共享状态,可以拷贝,共享某个共享状态的最终结果
shared_future 可以通过 future 对象隐式转换
也可以通过显式调用std::future::share显式转换
注意:当future转换为shared_future后,原future对象就会变的无效。
// 隐式转换
// 将std::shared_future调整为std::future也是正确的
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();
和future是一样的,还是只有四种方式get,wait,wait_for,wait_until
shared_future和future的差别就体现在shared 分享,其他都是一样的
future error类定义了一个异常对象,
当线程库中处理异步执行和共享状态的函数失败时抛出该异常对象
你可能会见过这样一个错误
terminate called after throwing an instance of 'std::future_error'
what(): std::future_error: Promise already satisfied
举一些会报错的例子就准备结束了,下面这些例子来自这篇好文
同时加了一些自己的注释
// 当future状态不是ready时抛错
#include
#include
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
*/
参考
如有错误,还请指正