为什么从' std::async '返回的future函数的析构函数会阻塞?



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

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

对我来说,这似乎是标准的c++ 11异步启动任务而不关心结果的方式。相反,为了实现这一点,必须显式地创建和分离线程(参见上述问题的答案)。

所以这是我的问题:关于std::future的析构函数必须阻塞的安全性/正确性,是否有任何原因?如果它只阻塞get,否则,如果我对返回值或异常不感兴趣,它就会被触发并忘记,这还不够吗?

阻塞由std::async和线程返回的未来函数的析构函数:这是一个有争议的话题。下面的论文列表按时间顺序反映了委员会成员的一些讨论:

  • N2802:请求重新考虑线程对象的on-destruction
  • N3630: async, ~future, and ~thread (Revision 1) by Herb Sutter
  • N3636: ~线程应该由Herb Sutter加入
  • N3637: async and ~future (Revision 3) by Herb Sutter, Chandler Carruth, Niklas Gustafsson
  • N3679: Async()未来析构函数必须等待
  • N3773: async and ~future (Revision 4) by Herb Sutter, Chandler Carruth, Niklas Gustafsson
  • N3776: Herb Sutter关于未来的措辞
  • N3777: Herb Sutter关于弃用async的措辞

虽然有很多讨论,但c++ 14没有计划对std::futurestd::thread的析构函数的阻塞行为进行更改。

关于你的问题,最有趣的可能是Hans Boehm的第二篇论文。我引用一些部分来回答你的问题。

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

[. .async()使用async启动策略返回的期货在析构函数中等待关联的共享状态准备好。这可以防止相关线程继续运行,并且由于关联的future已被销毁而不再有办法等待它完成的情况。这样的"失控"线程可以在它所依赖的对象的生命周期之后继续运行。

(例子)

最终结果很可能是跨线程的"内存破坏"。如果get()wait()被称为[..]在他们(未来)被摧毁之前。困难在于……是一个意想不到的异常可能会导致代码被绕过。因此,通常需要某种范围保护来确保安全。如果程序员忘记添加作用域保护,那么攻击者很可能会在适当的时候生成一个bad_alloc异常来利用这个疏忽,从而导致堆栈被覆盖。还可以控制用于覆盖堆栈的数据,从而获得对进程的控制。这是一个非常微妙的错误,根据我们的经验,它很可能在实际代码中被忽略。

更新: Michael Wong的行程报告也包含了一些关于2013年9月会议成果的有趣信息:

来自2013年9月c++标准会议的观点2/2

关于异步析构函数不应该阻塞的问题,我们对此进行了大量的讨论。(. .唯一得到相当支持的立场是……给出建议,未来的析构函数将不会阻塞,除非从async返回,使其成为值得注意的异常。(. .经过大量的讨论,我们唯一试图进位的部分是N3776,试图澄清~future~shared_future不会阻塞的位置,除非有async存在。有人尝试在c中发布弃用。弃用async而不替换。这个动议实际上几乎被提出了。但. .它还没上手术台就死了。

最新更新