问题 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)
摘自:2013 年 9 月 C++ 标准会议的观点,第 2 部分(共 2 部分)。
\n\n\n\n\n关于异步析构函数不应阻塞的问题,我们进行了大量的讨论。[..]唯一获得\n 大量支持的立场是[..] 给出建议,表明未来的析构函数\n 不会阻塞,除非从异步返回,这使其成为值得注意的\n 例外。[..]经过大量讨论,我们\n尝试携带的唯一部分是N3776,试图澄清\n ~future和~shared_future不\xe2\x80\x99t阻止的位置,除非可能存在\n异步的。尝试按照 C. 弃用异步而不进行替换的方式发出弃用。这个议案其实已经差点被提出了。但是[..]它甚至在到达手术台之前就死了。
\n
另请检查:N3679:Async() 未来的析构函数必须等待
\n\n\n\n基本问题
\n\n由具有异步启动策略的 async() 返回的 Future 在其析构函数中等待关联的共享状态准备就绪。这可以防止关联线程继续运行的情况,并且不再有办法等待它完成,因为关联的 future 已被破坏。如果不付出巨大的努力来等待完成,这样的“失控”线程可能会继续运行,超过它所依赖的对象的生命周期。
\n\n例如,考虑以下一对函数:
\n\nRun Code Online (Sandbox Code Playgroud)\n\nvoid 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如果 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
| 归档时间: |
|
| 查看次数: |
2136 次 |
| 最近记录: |