引发异常时调用 std::future 析构函数

Calling the std::future destructor when an exception is thrown

提问人:Andrey Epifantsev 提问时间:11/8/2023 更新时间:11/8/2023 访问量:53

问:

有这样的代码:

 #include <chrono>
 #include <future>
 #include <iostream>

 using namespace std::chrono_literals;

 int main()
 {
     try {
         std::atomic<bool> stopTask;
         stopTask.store(false, std::memory_order_seq_cst);

         auto future = std::async([&stopTask]() {
             for (int i = 0; i < 20; ++i)
             {
                 if (stopTask.load(std::memory_order_seq_cst))
                     break;
                 std::this_thread::sleep_for(500ms); // Imitation of useful task.
             }
         });

         // Some useful work in the main thread.
         throw std::runtime_error("Error"); // Oops! Something went wrong in the main thread.

         // Normal shutdown.
         stopTask.store(true, std::memory_order_seq_cst);
         future.get();
     }
     catch (...)
     {
         std::cout << "Exception caught" << std::endl;
     }
 }

我使用 std::async 在单独的线程中运行一个长时间运行的任务,并在其上接收 std::future。之后,在主线程中抛出异常。堆栈展开开始,并调用析构函数。一旦它到达 std::future 析构函数,堆栈展开就会停止,主线程就会阻塞,直到第二个线程完成。

看起来有些不对劲。我看到过一些建议,即尽可能快地制作析构函数,而不是在其中执行长时间的操作。但是这里的析构函数非常慢,堆栈展开需要很长时间。

问题:堆垛放卷需要这么长时间是正确的吗?也许我做错了什么,实际上需要做一些不同的事情?在这种情况下,最佳实践是什么?


在这个特定的代码中,std::future 析构函数可以通过像 RAII 一样围绕 std::atomic stopTask 创建一个包装类来加速,这会在析构函数中将 stopTask 设置为 true。但执行析构函数仍可能需要长达 500 毫秒的时间。这不能再加快了,因为在我的应用程序中,真正的最小操作需要很多时间。

C++ 多线程 C++11 异常

评论

1赞 user4581301 11/8/2023
方便的阅读,证实你的怀疑:en.cppreference.com/w/cpp/thread/future/%7Efuture。看起来您有一个创建者,它只有一个所有者,并且可能还没有完成。futurestd::async
0赞 NathanOliver 11/8/2023
看起来您需要手动创建一个 promise,从中获取 future,然后在分离的线程上运行它。

答: 暂无答案