future_error详解
引言
遇到这个问题以后第一次没有得到解答 随后经测试和查阅资料知道了它的由来 同时列出三种导致抛出future_error的原因 希望能有相同问题的朋友能得到帮助
什么是future_error
这是官网对于这个类型的解释
The class std::future_error defines an exception object that is thrown on failure by the functions in the thread library that deal with asynchronous execution and shared states
我们可以看到它是在处理异步程序和shared 状态失败的时候会被抛出.而且它本身是一个各个系统上实现不一致的东西,即ub
我们来看看官网的演示demo
#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";
}
}
我们可以看到在future的状态不是ready的时候我们进行get操作是ub,但是一些实现则会抛错,那么还有哪些情况会抛错呢
我们来简单的看看future的实现
void
set_value(const _Res& __r)
{ _M_future->_M_set_result(_State::__setter(this, __r)); }
void
set_value(_Res&& __r)
{ _M_future->_M_set_result(_State::__setter(this, std::move(__r))); }
void
set_exception(exception_ptr __p)
{ _M_future->_M_set_result(_State::__setter(__p, this)); }
--------------------------------------------------------------------
_M_set_result(function<_Ptr_type()> __res, bool __ignore_failure = false)
{
bool __did_set = false;
// all calls to this function are serialized,
// side-effects of invoking __res only happen once
call_once(_M_once, &_State_baseV2::_M_do_set, this,
std::__addressof(__res), std::__addressof(__did_set));
if (__did_set)
// Use release MO to synchronize with observers of the ready state.
_M_status._M_store_notify_all(_Status::__ready,
memory_order_release);
else if (!__ignore_failure)
__throw_future_error(int(future_errc::promise_already_satisfied));
}
我们可以看到在设置值的过程中函数都调用了M_set_result这个函数,而这个函数中使用了call_once,这个函数第一个参数是once_flag类型的,作用是这个函数在这段代码中代码仅调用一次,之后调用失败,
void
_M_do_set(function<_Ptr_type()>* __f, bool* __did_set)
{
_Ptr_type __res = (*__f)();
// Notify the caller that we did try to set; if we do not throw an
// exception, the caller will be aware that it did set (e.g., see
// _M_set_result).
*__did_set = true;
_M_result.swap(__res); // nothrow
}
我们可以看到如果成功执行了call_once,也就是第一次调用call_once,就会使dis_set为true,从而进入第一个判断条件,但是第后面调用的时候就会进入第二个判断条件,直接抛出一个future_error的错误,这也是一个抛错的时机 下面是一个demo
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;
}
terminate called after throwing an instance of 'std::future_error'
what(): std::future_error: Promise already satisfied
[1] 12731 abort ./a.out
随着两次的set_value 当然会抛错了.
再来看一种情况
template<typename _Tp>
static void
_S_check(const shared_ptr<_Tp>& __p)
{
if (!static_cast<bool>(__p))
__throw_future_error((int)future_errc::no_state);
}
我们可以看到这个函数中也会抛出future_error 且判断条件只有一句,很巧,找到了另一个成员函数
bool
valid() const noexcept { return static_cast<bool>(_M_state); }
在官网上我们找到了这样一段解释
Checks if the future refers to a shared state.
This is the case only for futures that were not default-constructed or moved from (i.e. returned by std::promise::get_future(), std::packaged_task::get_future() or std::async()) until the first time get() or share() is called.
The behavior is undefined if any member function other than the destructor, the move-assignment operator, or valid is called on a future that does not refer to shared state (although implementations are encouraged to throw std::future_error indicating no_state in this case). It is valid to move from a future object for which valid() is false
检测future是否指向共享状态
这仅适用于为非默认构造的或是在第一次执行get或者share之前未移动的future(返回 std::promise::get_future(), std::packaged_task::get_future() 或者std::async()算是移动),
如果在未引用共享状态的将来调用除析构函数,移动运算符或valid以外的任何成员函数,则该行为未定义(),尽管在这种情况下有些实现会鼓励抛出一个future_error来指示no_state,从valid()返回为false的对象移动的对象是有效的.
下面一个简单的小demo来模拟这种情况
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;
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
在get后执行了get_future 抛出了错误