从 std::async 返回的 std::future 在超出范围时挂起
std::future returned from std::async hangs while going out of scope
我正在使用 std::async
和 C++ 11
中的 std::future
的组合。我正在使用我在我的代码中对某个 activity 执行 time_out,这 可能 在我尝试连接到服务器时需要时间。
代码如下:
#include <future>
#include <chrono>
std::size_t PotentiallyLongRunningActivity() {
using namespace std::chrono_literals;
std::this_thread::sleep_for(10000s);
return 10;
}
bool DoActivity() {
bool activity_done = false;
auto my_future_result(std::async(std::launch::async, []() {
return PotentiallyLongRunningActivity(); //returns size_t
}));
std::future_status my_future_status = my_future_result.wait_for(std::chrono::milliseconds(800));
if (my_future_status == std::future_status::timeout) {
activity_done = false;
}
else if (my_future_status == std::future_status::ready) {
if (my_future_result.valid() && my_future_result.get() > 0) {
activity_done = true;
}
}
return activity_done;
//my_future_result hangs while exiting this method !!!
}
int main(int argc, char *argv[])
{
DoActivity();
return 0;
}
大多数情况下一切正常。在许多情况下,未来超时并报告准备就绪。但是,我观察到的奇怪行为是,在某些情况下 UI 挂起,因为 my_future_result
在超出范围时挂起。我通过重复调用 my_future_result.get()
来确认这一点,如果在退出该方法之前调用,则永远不会 returns 。
我该如何解决这个问题?有什么方法可以取消、删除或终止 std::future
?
您正在 std::async
任务完成之前退出函数。在某些情况下,std::future
的析构函数将阻塞,直到任务完成。
http://en.cppreference.com/w/cpp/thread/future/wait_for
在 wait_for
的文档中,示例显示了在超时后对 wait_for
的多次调用,表明超时操作不会取消 std::async
任务。
没有 built-in 支持(我可以发现)允许在外部终止线程。这是有道理的,因为如果线程以这种方式终止,则无法正确清理线程正在使用的系统资源的状态。
相反,最好将超时逻辑放在线程本身中,这样它就可以自行终止并正确清理。
取自 ,只有 "the start"、"f2 finished" 和 "the end" 会从此代码中打印出来(因为 f1
不会 "hang"):
#include <future>
#include <thread>
#include <iostream>
int main() {
using namespace std::literals;
{
std::packaged_task<int()> task([]() {
std::this_thread::sleep_for(5s);
std::cout << "f1 finished" << std::endl;
return 42;
});
std::future<int> f1 = task.get_future();
std::thread(std::move(task)).detach();
std::future<int> f2 = std::async(std::launch::async, []() {
std::this_thread::sleep_for(3s);
std::cout << "f2 finished" << std::endl;
return 42;
});
f1.wait_for(1s);
f2.wait_for(1s);
std::cout << "the start" << std::endl;
}
// std::this_thread::sleep_for(7s);
std::cout << "the end" << std::endl;
}
有关良好的讨论,请参阅:http://scottmeyers.blogspot.com.br/2013/03/stdfutures-from-stdasync-arent-special.html。
C++ 标准库不支持线程终止操作。
注意你的线程detach
。分离本身不是 "extremely bad",它可能在用户可终止的守护进程中很有用,或者如果您有其他关于编排和拆卸的想法。否则,detach
将没有意义由标准库提供。
一般来说,失去对线程的跟踪是非常糟糕的。当 main
退出时在另一个线程中有代码 运行ning 是未定义行为的接受者。
因此,来自 std::async
的 returns 的 std::future
具有特殊的 属性,它将等待 std::async
完成销毁。
这就是您所说的 "hang"。这不是挂起 -- 它正在等待任务完成。
C++11 中的线程原语是原语;它们不是完整功能应用程序的完成类型。您可以使用它们来编写线程池、延迟任务队列等;天真地使用它们 "in the raw" 往往会导致它们偏向于正确性,而不是给你你想要的。
一个简单的线程池就是:
template<class T>
struct threaded_queue {
using lock = std::unique_lock<std::mutex>;
void push_back( T t ) {
{
lock l(m);
data.push_back(std::move(t));
}
cv.notify_one();
}
boost::optional<T> pop_front() {
lock l(m);
cv.wait(l, [this]{ return abort || !data.empty(); } );
if (abort) return {};
auto r = std::move(data.back());
data.pop_back();
return std::move(r);
}
void terminate() {
{
lock l(m);
abort = true;
data.clear();
}
cv.notify_all();
}
~threaded_queue()
{
terminate();
}
private:
std::mutex m;
std::deque<T> data;
std::condition_variable cv;
bool abort = false;
};
struct thread_pool {
thread_pool( std::size_t n = 1 ) { start_thread(n); }
thread_pool( thread_pool&& ) = delete;
thread_pool& operator=( thread_pool&& ) = delete;
~thread_pool() = default; // or `{ terminate(); }` if you want to abandon some tasks
template<class F, class R=std::result_of_t<F&()>>
std::future<R> queue_task( F task ) {
std::packaged_task<R()> p(std::move(task));
auto r = p.get_future();
tasks.push_back( std::move(p) );
return r;
}
template<class F, class R=std::result_of_t<F&()>>
std::future<R> run_task( F task ) {
if (threads_active() >= total_threads()) {
start_thread();
}
return queue_task( std::move(task) );
}
void terminate() {
tasks.terminate();
}
std::size_t threads_active() const {
return active;
}
std::size_t total_threads() const {
return threads.size();
}
void clear_threads() {
terminate();
threads.clear();
}
void start_thread( std::size_t n = 1 ) {
while(n-->0) {
threads.push_back(
std::async( std::launch::async,
[this]{
while(auto task = tasks.pop_front()) {
++active;
try{
(*task)();
} catch(...) {
--active;
throw;
}
--active;
}
}
)
);
}
}
private:
std::vector<std::future<void>> threads;
threaded_queue<std::packaged_task<void()>> tasks;
std::atomic<std::size_t> active;
};
现在您在某处创建了一些线程池,向其发送任务,然后您可以等待相关的未来。池中的线程数量有限。
run_task
将确保有线程 运行 您排队的任何任务。 queue_task
将仅使用可用的现有线程。
返回的std::future<void>
不会阻塞任务完成;但是 thread_pool
对象的析构函数会。
请注意,它将中止所有排队的任务,并等待当前 运行ing 任务完成,默认为销毁。
包裹 unique_ptr<thread_pool>
的东西有助于方便移动。必须禁用移动,因为活动线程保持 pointer-to-this
.
具有讽刺意味的是,thread_pool
不是线程安全的;这是因为我们没有保护 std::vector<std::future<void>> threads;
;我的意思是,除了线程本身存储的线程安全之外。它被设计为只能由一个外部线程直接访问。
queue_task
和 terminate
是线程安全的,这主要是偶然的。
错误的原因是因为编译器没有被告知函数 DoModify() 的结果将异步可用(因此被声明为 std::future<> )并且它期望一个同步结果没有到达的 bool 类型。您可以使用 std::future::is_ready() 或 std::future_status 。这里有一个示例代码片段
std::future<size_t> DoActivity()
{
return std::async(std::launch::async, []()
{
return PotentiallyLongRunningActivity();
});
}
int main()
{
auto result = DoActivity();
if ( result. Is_ready() )
{
auto data = result.get();
//do something with data
}
}
我正在使用 std::async
和 C++ 11
中的 std::future
的组合。我正在使用我在我的代码中对某个 activity 执行 time_out,这 可能 在我尝试连接到服务器时需要时间。
代码如下:
#include <future>
#include <chrono>
std::size_t PotentiallyLongRunningActivity() {
using namespace std::chrono_literals;
std::this_thread::sleep_for(10000s);
return 10;
}
bool DoActivity() {
bool activity_done = false;
auto my_future_result(std::async(std::launch::async, []() {
return PotentiallyLongRunningActivity(); //returns size_t
}));
std::future_status my_future_status = my_future_result.wait_for(std::chrono::milliseconds(800));
if (my_future_status == std::future_status::timeout) {
activity_done = false;
}
else if (my_future_status == std::future_status::ready) {
if (my_future_result.valid() && my_future_result.get() > 0) {
activity_done = true;
}
}
return activity_done;
//my_future_result hangs while exiting this method !!!
}
int main(int argc, char *argv[])
{
DoActivity();
return 0;
}
大多数情况下一切正常。在许多情况下,未来超时并报告准备就绪。但是,我观察到的奇怪行为是,在某些情况下 UI 挂起,因为 my_future_result
在超出范围时挂起。我通过重复调用 my_future_result.get()
来确认这一点,如果在退出该方法之前调用,则永远不会 returns 。
我该如何解决这个问题?有什么方法可以取消、删除或终止 std::future
?
您正在 std::async
任务完成之前退出函数。在某些情况下,std::future
的析构函数将阻塞,直到任务完成。
http://en.cppreference.com/w/cpp/thread/future/wait_for
在 wait_for
的文档中,示例显示了在超时后对 wait_for
的多次调用,表明超时操作不会取消 std::async
任务。
没有 built-in 支持(我可以发现)允许在外部终止线程。这是有道理的,因为如果线程以这种方式终止,则无法正确清理线程正在使用的系统资源的状态。
相反,最好将超时逻辑放在线程本身中,这样它就可以自行终止并正确清理。
取自 f1
不会 "hang"):
#include <future>
#include <thread>
#include <iostream>
int main() {
using namespace std::literals;
{
std::packaged_task<int()> task([]() {
std::this_thread::sleep_for(5s);
std::cout << "f1 finished" << std::endl;
return 42;
});
std::future<int> f1 = task.get_future();
std::thread(std::move(task)).detach();
std::future<int> f2 = std::async(std::launch::async, []() {
std::this_thread::sleep_for(3s);
std::cout << "f2 finished" << std::endl;
return 42;
});
f1.wait_for(1s);
f2.wait_for(1s);
std::cout << "the start" << std::endl;
}
// std::this_thread::sleep_for(7s);
std::cout << "the end" << std::endl;
}
有关良好的讨论,请参阅:http://scottmeyers.blogspot.com.br/2013/03/stdfutures-from-stdasync-arent-special.html。
C++ 标准库不支持线程终止操作。
注意你的线程detach
。分离本身不是 "extremely bad",它可能在用户可终止的守护进程中很有用,或者如果您有其他关于编排和拆卸的想法。否则,detach
将没有意义由标准库提供。
一般来说,失去对线程的跟踪是非常糟糕的。当 main
退出时在另一个线程中有代码 运行ning 是未定义行为的接受者。
因此,来自 std::async
的 returns 的 std::future
具有特殊的 属性,它将等待 std::async
完成销毁。
这就是您所说的 "hang"。这不是挂起 -- 它正在等待任务完成。
C++11 中的线程原语是原语;它们不是完整功能应用程序的完成类型。您可以使用它们来编写线程池、延迟任务队列等;天真地使用它们 "in the raw" 往往会导致它们偏向于正确性,而不是给你你想要的。
一个简单的线程池就是:
template<class T>
struct threaded_queue {
using lock = std::unique_lock<std::mutex>;
void push_back( T t ) {
{
lock l(m);
data.push_back(std::move(t));
}
cv.notify_one();
}
boost::optional<T> pop_front() {
lock l(m);
cv.wait(l, [this]{ return abort || !data.empty(); } );
if (abort) return {};
auto r = std::move(data.back());
data.pop_back();
return std::move(r);
}
void terminate() {
{
lock l(m);
abort = true;
data.clear();
}
cv.notify_all();
}
~threaded_queue()
{
terminate();
}
private:
std::mutex m;
std::deque<T> data;
std::condition_variable cv;
bool abort = false;
};
struct thread_pool {
thread_pool( std::size_t n = 1 ) { start_thread(n); }
thread_pool( thread_pool&& ) = delete;
thread_pool& operator=( thread_pool&& ) = delete;
~thread_pool() = default; // or `{ terminate(); }` if you want to abandon some tasks
template<class F, class R=std::result_of_t<F&()>>
std::future<R> queue_task( F task ) {
std::packaged_task<R()> p(std::move(task));
auto r = p.get_future();
tasks.push_back( std::move(p) );
return r;
}
template<class F, class R=std::result_of_t<F&()>>
std::future<R> run_task( F task ) {
if (threads_active() >= total_threads()) {
start_thread();
}
return queue_task( std::move(task) );
}
void terminate() {
tasks.terminate();
}
std::size_t threads_active() const {
return active;
}
std::size_t total_threads() const {
return threads.size();
}
void clear_threads() {
terminate();
threads.clear();
}
void start_thread( std::size_t n = 1 ) {
while(n-->0) {
threads.push_back(
std::async( std::launch::async,
[this]{
while(auto task = tasks.pop_front()) {
++active;
try{
(*task)();
} catch(...) {
--active;
throw;
}
--active;
}
}
)
);
}
}
private:
std::vector<std::future<void>> threads;
threaded_queue<std::packaged_task<void()>> tasks;
std::atomic<std::size_t> active;
};
现在您在某处创建了一些线程池,向其发送任务,然后您可以等待相关的未来。池中的线程数量有限。
run_task
将确保有线程 运行 您排队的任何任务。 queue_task
将仅使用可用的现有线程。
返回的std::future<void>
不会阻塞任务完成;但是 thread_pool
对象的析构函数会。
请注意,它将中止所有排队的任务,并等待当前 运行ing 任务完成,默认为销毁。
包裹 unique_ptr<thread_pool>
的东西有助于方便移动。必须禁用移动,因为活动线程保持 pointer-to-this
.
thread_pool
不是线程安全的;这是因为我们没有保护 std::vector<std::future<void>> threads;
;我的意思是,除了线程本身存储的线程安全之外。它被设计为只能由一个外部线程直接访问。
queue_task
和 terminate
是线程安全的,这主要是偶然的。
错误的原因是因为编译器没有被告知函数 DoModify() 的结果将异步可用(因此被声明为 std::future<> )并且它期望一个同步结果没有到达的 bool 类型。您可以使用 std::future::is_ready() 或 std::future_status 。这里有一个示例代码片段
std::future<size_t> DoActivity()
{
return std::async(std::launch::async, []()
{
return PotentiallyLongRunningActivity();
});
}
int main()
{
auto result = DoActivity();
if ( result. Is_ready() )
{
auto data = result.get();
//do something with data
}
}