boost::future 和 std::future 的不同行为
Different behavior of boost::future and std::future
具体来说,boost::future
似乎没有在析构函数中阻塞。还有 boost documentation didn't mention it. However std::future
did mention it.
如果我想将一些 .then()
链接到 boost::future
,我还需要在末尾链接一个 .get()
调用以强制临时对象阻塞以获得正确的行为。这是正确的使用方式吗?
void a()
{
boost::async([] {
boost::this_thread::sleep_for(boost::chrono::seconds{1});
std::cout << "Finished sleeping\n";
return 1;
});
std::cout << "End of a()\n";
}
void b()
{
std::async([] {
std::this_thread::sleep_for(std::chrono::seconds{1});
std::cout << "Finished sleeping\n";
return 1;
});
std::cout << "End of b()\n";
}
int main()
{
a();//No finished sleeping printed
std::cout << "End of main()\n";
}
int main()
{
b();//finished sleeping will print
std::cout << "End of main()\n";
}
https://www.boost.org/doc/libs/1_75_0/doc/html/thread/synchronization.html
The returned futures behave as the ones returned from boost::async, the destructor of the future object returned from then will block. This could be subject to change in future versions.
(2015) https://www.boost.org/doc/libs/1_75_0/doc/html/thread/changes.html
Version 4.5.0 - boost 1.58
Fixed Bugs:
#10968 The futures returned by async() and future::then() are not blocking.
(2017) https://github.com/boostorg/thread/issues/194
I'm aware of the issue. Boost.thread did it by design following std::async and the first proposals of std::future::then.
The problem is that changing the behavior (even if it is not desired in some contexts) would be a breaking change. I would need to crate a new version, but Idon't want to create a new version using compiler flags as this has become a nightmare to maintain.
So I will need to create a boost/thread_v5 which will produce different binaries lib_boost_thread_v5.a. Boost.Threads is not structured this way, and this will mean a lot of work.
This is the single way to avoid breaking changes, but before doing that I would like to enumerate all the breaking changes that should be on this new version.
从 async
中 return 得到的 future 对象的析构函数似乎在 2017 年之后的某个时间点真的变成了非阻塞的,所以你 应该使用 .get()
阻止.
我不能阻止它因为:
BOOST_THREAD_FUTURE_BLOCKING
(未记录)并非在所有情况下都是 #define
d;
- 如果你手动
#define BOOST_THREAD_FUTURE_BLOCKING
,你可以看到当 future
被销毁时 ~future_async_shared_state_base()
将被调用 和 lambda 传递给async 已经完成 运行(即当 boost::shared_ptr
的 use_count 从 2 下降到 1 再到 0),所以未来对象的析构函数 return 从 async
(当boost::shared_ptr
的use_count从2降到1时)仍然不会阻塞。
https://www.boost.org/doc/libs/1_75_0/boost/thread/detail/config.hpp
#if BOOST_THREAD_VERSION>=5
//#define BOOST_THREAD_FUTURE_BLOCKING
#if ! defined BOOST_THREAD_PROVIDES_EXECUTORS \
&& ! defined BOOST_THREAD_DONT_PROVIDE_EXECUTORS
#define BOOST_THREAD_PROVIDES_EXECUTORS
#endif
#else
//#define BOOST_THREAD_FUTURE_BLOCKING
#define BOOST_THREAD_ASYNC_FUTURE_WAITS
#endif
https://www.boost.org/doc/libs/1_75_0/boost/thread/future.hpp
在 async
:
void init(BOOST_THREAD_FWD_REF(Fp) f)
{
#ifdef BOOST_THREAD_FUTURE_BLOCKING
this->thr_ = boost::thread(&future_async_shared_state::run, static_shared_from_this(this), boost::forward<Fp>(f));
#else
boost::thread(&future_async_shared_state::run, static_shared_from_this(this), boost::forward<Fp>(f)).detach();
#endif
}
当 future
被销毁时 和 传递给异步的 lambda 已完成 运行:
~future_async_shared_state_base()
{
#ifdef BOOST_THREAD_FUTURE_BLOCKING
join();
#elif defined BOOST_THREAD_ASYNC_FUTURE_WAITS
unique_lock<boost::mutex> lk(this->mutex);
this->waiters.wait(lk, boost::bind(&shared_state_base::is_done, boost::ref(*this)));
#endif
}
C++20 已经转向协程,它能够在比 future.then 更复杂的情况下拆分控制流:CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"。您可以尝试像 cppcoro 或 boost.fiber 这样的库,并在复杂的控制结构中暂停任务函数(例如 co_await
inside if
inside while
inside for
)。
C++23 Executor TS:在 A Unified Executors Proposal for C++ 的初始版本中有 OneWayExecutor
execute
(即发即弃)TwoWayExecutor
twoway_execute
(return 一个未来)then_execute
(取一个未来和一个函数,return 一个未来)等等,但在最近的版本中,它们被发送者和接收者所取代。
具体来说,boost::future
似乎没有在析构函数中阻塞。还有 boost documentation didn't mention it. However std::future
did mention it.
如果我想将一些 .then()
链接到 boost::future
,我还需要在末尾链接一个 .get()
调用以强制临时对象阻塞以获得正确的行为。这是正确的使用方式吗?
void a()
{
boost::async([] {
boost::this_thread::sleep_for(boost::chrono::seconds{1});
std::cout << "Finished sleeping\n";
return 1;
});
std::cout << "End of a()\n";
}
void b()
{
std::async([] {
std::this_thread::sleep_for(std::chrono::seconds{1});
std::cout << "Finished sleeping\n";
return 1;
});
std::cout << "End of b()\n";
}
int main()
{
a();//No finished sleeping printed
std::cout << "End of main()\n";
}
int main()
{
b();//finished sleeping will print
std::cout << "End of main()\n";
}
https://www.boost.org/doc/libs/1_75_0/doc/html/thread/synchronization.html
The returned futures behave as the ones returned from boost::async, the destructor of the future object returned from then will block. This could be subject to change in future versions.
(2015) https://www.boost.org/doc/libs/1_75_0/doc/html/thread/changes.html
Version 4.5.0 - boost 1.58
Fixed Bugs:
#10968 The futures returned by async() and future::then() are not blocking.
(2017) https://github.com/boostorg/thread/issues/194
I'm aware of the issue. Boost.thread did it by design following std::async and the first proposals of std::future::then.
The problem is that changing the behavior (even if it is not desired in some contexts) would be a breaking change. I would need to crate a new version, but Idon't want to create a new version using compiler flags as this has become a nightmare to maintain.
So I will need to create a boost/thread_v5 which will produce different binaries lib_boost_thread_v5.a. Boost.Threads is not structured this way, and this will mean a lot of work.
This is the single way to avoid breaking changes, but before doing that I would like to enumerate all the breaking changes that should be on this new version.
从 async
中 return 得到的 future 对象的析构函数似乎在 2017 年之后的某个时间点真的变成了非阻塞的,所以你 应该使用 .get()
阻止.
我不能阻止它因为:
BOOST_THREAD_FUTURE_BLOCKING
(未记录)并非在所有情况下都是#define
d;- 如果你手动
#define BOOST_THREAD_FUTURE_BLOCKING
,你可以看到当future
被销毁时~future_async_shared_state_base()
将被调用 和 lambda 传递给async 已经完成 运行(即当boost::shared_ptr
的 use_count 从 2 下降到 1 再到 0),所以未来对象的析构函数 return 从async
(当boost::shared_ptr
的use_count从2降到1时)仍然不会阻塞。
https://www.boost.org/doc/libs/1_75_0/boost/thread/detail/config.hpp
#if BOOST_THREAD_VERSION>=5
//#define BOOST_THREAD_FUTURE_BLOCKING
#if ! defined BOOST_THREAD_PROVIDES_EXECUTORS \
&& ! defined BOOST_THREAD_DONT_PROVIDE_EXECUTORS
#define BOOST_THREAD_PROVIDES_EXECUTORS
#endif
#else
//#define BOOST_THREAD_FUTURE_BLOCKING
#define BOOST_THREAD_ASYNC_FUTURE_WAITS
#endif
https://www.boost.org/doc/libs/1_75_0/boost/thread/future.hpp
在 async
:
void init(BOOST_THREAD_FWD_REF(Fp) f)
{
#ifdef BOOST_THREAD_FUTURE_BLOCKING
this->thr_ = boost::thread(&future_async_shared_state::run, static_shared_from_this(this), boost::forward<Fp>(f));
#else
boost::thread(&future_async_shared_state::run, static_shared_from_this(this), boost::forward<Fp>(f)).detach();
#endif
}
当 future
被销毁时 和 传递给异步的 lambda 已完成 运行:
~future_async_shared_state_base()
{
#ifdef BOOST_THREAD_FUTURE_BLOCKING
join();
#elif defined BOOST_THREAD_ASYNC_FUTURE_WAITS
unique_lock<boost::mutex> lk(this->mutex);
this->waiters.wait(lk, boost::bind(&shared_state_base::is_done, boost::ref(*this)));
#endif
}
C++20 已经转向协程,它能够在比 future.then 更复杂的情况下拆分控制流:CppCon 2015: Gor Nishanov “C++ Coroutines - a negative overhead abstraction"。您可以尝试像 cppcoro 或 boost.fiber 这样的库,并在复杂的控制结构中暂停任务函数(例如 co_await
inside if
inside while
inside for
)。
C++23 Executor TS:在 A Unified Executors Proposal for C++ 的初始版本中有 OneWayExecutor
execute
(即发即弃)TwoWayExecutor
twoway_execute
(return 一个未来)then_execute
(取一个未来和一个函数,return 一个未来)等等,但在最近的版本中,它们被发送者和接收者所取代。