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

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

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

对我来说,这似乎是规范的C ++ 11方式asynchronous启动任务而不关心结果。 相反,人们必须明确地创build和分离一个线程(见提到的问题的答案 ),以实现这一点。

所以这里是我的问题:是否有任何std::future的析构函数被阻止的安全性/正确性的原因? 如果阻止get只是否是不够的,如果我不感兴趣的返回值或例外,它只是火灾和遗忘?

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

  • N2802: Hans Boehm 请求重新考虑线程对象的破坏
  • N3630: Herb Sutter的async,〜future和〜thread(Revision 1)
  • N3636:〜线应join草药萨特
  • N3637:由Herb Sutter,Chandler Carruth,Niklas Gustafsson 撰写的async和〜future(Revision 3)
  • N3679:Async()未来的破坏者必须由Hans Boehm 等待
  • N3773:由Herb Sutter,Chandler Carruth,Niklas Gustafsson 撰写的async和〜future(修订版4)
  • N3776: Herb Sutter的未来的措辞
  • N3777: Herb Sutter 不赞成asynchronous的措词

尽pipe讨论了很多,但是对于std :: futurestd :: thread的析构函数的阻塞行为没有针对C ++ 14计划。

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

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

[…] async启动策略async()返回的期货在其析构函数中等待关联的共享状态准备就绪。 这可以防止相关联的线程继续运行的情况,并且由于关联的未来已经被销毁,不再需要等待它完成的手段。 如果没有英勇的努力,否则等待完成,这样一个“逃跑”的线程可以继续运行超过它所依赖的对象的生命周期。

[例]

最终的结果很可能是一个跨线程的“内存粉碎”。 如果get()wait()在[期货]被销毁之前被调用[..],那么这个问题当然是可以避免的。 困难[..]是一个意外的exception可能导致代码被绕过。 因此通常需要某种范围的警戒来确保安全。 如果程序员忘记添加示波器后卫,攻击者可能会在适当的时机产生一个bad_allocexception,以利用这个监督,并导致堆栈被覆盖。 也可以控制用于覆盖堆栈的数据,从而获得对进程的控制权。 这是一个非常微妙的错误,根据我们的经验,它很可能在实际的代码中被忽略。

更新:王永平的旅程报告也包含一些有关2013年9月会议结果的有趣信息:

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

在asynchronous析构函数不应该阻塞的问题上,我们进行了大量的讨论。 […]得到大量支持的唯一立场是[…]提供咨询意见,未来的析构函数不会阻塞,除非从asynchronous返回,使其成为显着的例外。 经过大量的讨论,我们试图进行的唯一的部分是N3776,试图澄清~future和共享~future可能会存在的asynchronous情况。 有人企图按照C的方式发出弃用声明,而不用更换。 这个动议其实是差不多提出来的。 但是,它甚至在到达手术台之前就已经死亡了。