为什么从`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 :: future和std :: 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的方式发出弃用声明,而不用更换。 这个动议其实是差不多提出来的。 但是,它甚至在到达手术台之前就已经死亡了。