std::future 是否等待销毁

use*_*183 5 c++ concurrency

问题 std::future 在销毁时调用 wait() 或 get() 吗?

例子

void fun()
{
     std::future<int> fut = my_thread_pool.submit(some_work);
}//is fut.wait() or fut.get() called? here
Run Code Online (Sandbox Code Playgroud)

Rah*_*thi 4

摘自:2013 年 9 月 C++ 标准会议的观点,第 2 部分(共 2 部分)。

\n\n
\n

关于异步析构函数不应阻塞的问题,我们进行了大量的讨论。[..]唯一获得\n 大量支持的立场是[..] 给出建议,表明未来的析构函数\n 不会阻塞,除非从异步返回,这使其成为值得注意的\n 例外。[..]经过大量讨论,我们\n尝试携带的唯一部分是N3776,试图澄清\n ~future和~shared_future不\xe2\x80\x99t阻止的位置,除非可能存在\n异步的。尝试按照 C. 弃用异步而不进行替换的方式发出弃用。这个议案其实已经差点被提出了。但是[..]它甚至在到达手术台之前就死了。

\n
\n\n

另请检查:N3679:Async() 未来的析构函数必须等待

\n\n
\n

基本问题

\n\n

由具有异步启动策略的 async() 返回的 Future 在其析构函数中等待关联的共享状态准备就绪。这可以防止关联线程继续运行的情况,并且不再有办法等待它完成,因为关联的 future 已被破坏。如果不付出巨大的努力来等待完成,这样的“失控”线程可能会继续运行,超过它所依赖的对象的生命周期。

\n\n

例如,考虑以下一对函数:

\n\n
void f() {\n  vector<int> v;\n  ...\n  do_parallel_foo(v);\n  ...\n}\n\nvoid do_parallel_foo(vector<int>& v) {\n  auto fut = no_join_async([&] {...  foo(v); return ...; });\n  a: ...\n  fut.get();\n  ...\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果 no_join_async() 返回一个 future,其析构函数不等待\n 异步完成,则一切都可以正常运行,直到 a 处的代码抛出\n 异常。此时,不会等待异步完成,\n 并且它可能会继续运行超过 do_parallel_foo()\n 和 f() 的退出,导致异步任务访问并覆盖之前分配给 v 方式的内存\n已经过了它的一生。

\n\n

最终结果很可能是类似条件下 N2802 中描述的跨线程“内存粉碎”。

\n\n

如果在 no_join_async() 生成的 future 被销毁之前调用 get() 或 wait(),这个问题当然可以避免。与 N2802 一样,困难在于意外异常可能会导致该代码被绕过。因此,通常需要某种范围防护装置来确保安全。如果程序员忘记添加范围保护,则攻击者可能会在适当的时候生成例如 bad_alloc 异常,以利用监督,并导致堆栈被覆盖。还可以控制用于覆盖堆栈的数据,从而获得对进程的控制。这是一个非常微妙的错误,根据我们的经验,在实际代码中很可能会被忽略。

\n
\n