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

时间:2014-05-04 10:24:31

标签: c++ multithreading c++11 asynchronous std

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

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

对我而言,这似乎是规范的C ++ 11方式异步启动任务而不关心结果。相反,为了实现这一点,人们必须明确地创建和分离一个线程(参见answer提到的问题)。

所以我的问题是:std::future的析构函数是否必须阻塞,是否有任何关于安全性/正确性的理由?如果它只在get上阻止就不够了,否则,如果我对返回值或异常不感兴趣,那么它只是开火而忘记?

1 个答案:

答案 0 :(得分:30)

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

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

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

  

<强> N3679: Async() future destructors must wait

     

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

     

[实施例]

     

最终结果很可能是一个跨线程的“内存粉碎”。如果{[1}}或get()在[期货]被销毁之前被调用[..],当然可以避免这个问题。难点[..]是一个意外的异常可能导致绕过该代码。因此,通常需要某种防护罩以确保安全。如果程序员忘记添加范围防护,攻击者可能会生成例如在适当的时候利用bad_alloc异常来利用疏忽,并导致堆栈被覆盖。也可以控制用于覆盖堆栈的数据,从而获得对过程的控制。这是一个非常微妙的错误,根据我们的经验,它可能会在实际代码中被忽略。

更新: Michael Wong的旅行报告还包含一些有关2013年9月会议结果的有趣信息:

  

<强> The View from the C++ Standard meeting September 2013 Part 2 of 2.

     

关于异步析构函数不应该阻塞的问题,我们就此进行了大量讨论。 [..]获得相当大支持的唯一立场是[...]提供未来析构函数不会阻止的建议,除非从异步中返回,使其成为值得注意的例外。 [..]经过重要讨论后,我们尝试进行的唯一部分是N3776,试图阐明wait()~future除了可能存在异步之外不会阻止的位置。尝试按照C行发布弃用。不更换时弃用异步。这项动议实际上几乎是提出来的。但[..]它甚至在它到达手术台之前就已经死了。