为什么从`std :: async`阻塞返回的是将来的析构函数?

珍妮克·贝内特

当尝试回答另一个Stackoverflow问题时,我意识到这个简单的C ++ 11代码段隐式阻塞了调用线程:

std::async(std::launch::async, run_async_task)

在我看来,这似乎是规范的C ++ 11异步启动任务而不关心结果的方法。而是一个有明显显式地创建和分离线程(见答案所提到的问题),以实现这一目标。

所以这是我的问题:在安全性/正确性方面,是否有任何原因std::future必须阻止a的析构函数如果get阻塞则不足够,否则,如果我对返回值或异常不感兴趣,那简直就是开枪而忘了吗?

Nosid

阻止std :: async和线程返回的期货的析构函数:这是一个有争议的话题。以下按时间顺序排列的论文清单反映了委员会成员的一些讨论:

尽管进行了很多讨论,但是对于C ++ 14,没有计划对std :: futurestd :: thread的析构函数的阻塞行为进行任何更改

关于您的问题,最有趣的论文可能是汉斯·勃姆(Hans Boehm)的第二篇。我引用一些部分来回答您的问题。

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

[..]async()使用async启动策略返回的期货在其析构函数中等待关联的共享状态准备就绪。这样可以防止关联线程继续运行,并且不再有等待它完成的方法,因为关联的未来已被破坏。无需英勇的努力来等待完成,这样的“失控”线程可以继续运行超过它所依赖的对象的生存期。

[例子]

最终结果可能是跨线程“内存粉碎”。这个问题当然是可以避免的,如果get()还是wait()被称为[..]他们[期货]被破坏之前。困难[..]是意外的异常可能导致该代码被绕过。因此,通常需要某种示波器来确保安全。如果程序员忘记添加范围保护,则攻击者很可能会在适当的时候生成例如bad_alloc异常,以利用监督的优势,并导致堆栈被覆盖。也有可能控制用于覆盖堆栈的数据,从而获得对该过程的控制。根据我们的经验,这是一个足够细微的错误,在实际代码中很可能会忽略它。

更新:黄贯中的《旅行报告》还包含一些有关2013年9月会议结果的有趣信息:

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

关于异步析构函数不应阻止的问题,我们进行了大量讨论。[..]得到唯一支持的唯一位置是[..]提出建议,除非从异步返回,否则将来的析构函数将不会阻塞,这使其成为一个明显的例外。[..]显著讨论后,我们试图进位的唯一部分是N3776,企图澄清的位置~future~shared_future不除了可能在异步的存在阻止。尝试按照C的方式发出弃用。弃用异步而不替换。这项议案实际上几乎是提出来的。但是[..]它甚至在到达手术台之前就死了。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

是否可以在不调用 std async 的情况下使用阻塞析构函数创建 std 未来?

为什么在“返回 0”后调用析构函数?

为什么析构函数挂起

为什么要调用析构函数

为什么不调用析构函数?

为什么std :: vector的构造函数调用自定义类的析构函数?

当对象绑定到成员函数时,为什么std :: function会调用析构函数?

为什么`std :: exit`不能按预期触发析构函数?

为什么std :: unary_function不包含虚拟析构函数

为什么C ++标准为std :: bitset :: reference指定析构函数?

为什么std :: vector在离开其他作用域的同时调用析构函数?

为什么C ++不使用std :: nested_exception允许从析构函数中抛出?

为什么使用虚拟析构函数进行std :: vector :: push_back segfaults?

为什么在抛出我的析构函数时没有调用 std::terminate

为什么std :: shared_ptr两次调用我的析构函数?

为什么C ++析构函数会影响返回值优化的行为

如果未定义析构函数,为什么不进行返回值优化?

为什么析构函数比构造函数调用得更多?

为什么在Friend函数中调用析构函数

为什么“ = default”析构函数与隐式声明的析构函数不同?

什么时候调用std :: thread析构函数?

为什么要在std :: vector :: push_back(T object)方法中构造对象时调用析构函数?

只需添加不执行任何操作都可能导致编译错误的析构函数(在std :: move附近),为什么?

为什么纯虚拟析构函数需要实现

为什么虚拟析构函数写入内存?

为什么析构函数可以更改常量对象的状态?

为什么不调用继承类的析构函数?

为什么在父类之前调用子类的析构函数?

为什么代码两次调用析构函数?