有没有一种安全的方法可以在std :: future上调用wait()?

时间:2014-12-11 12:59:05

标签: c++ c++11

C ++ 11标准说:

  

30.6.6课程模板未来

     

(3)"调用析构函数以外的任何成员函数的效果,   move-assignment运算符,或者对将来的对象有效   valid() == false未定义。"

那么,这是否意味着以下代码可能会遇到未定义的行为?

void wait_for_future(std::future<void> & f)
{
    if (f.valid()) {
        // what if another thread meanwhile calls get() on f (which invalidates f)?
        f.wait();
    }
    else {
        return;
    }
}

Q1:这真的是一种可能的未定义行为吗?

Q2:是否有任何符合标准的方法来避免可能的未定义行为?

请注意,该标准有一个有趣的注释[也在30.6.6(3)]:

  

&#34; [注意:鼓励实施   检测这种情况并抛出一个类型为future_error的对象   future_errc::no_state的错误条件。 -endnote]&#34;

问题3:如果我只依赖标准笔记,只使用f.wait()而不检查f的有效性,这样可以吗?

void wait_for_future(std::future<void> & f)
{
    try {
        f.wait();
    }
    catch (std::future_error const & err) {
        return;
    }
}

编辑:收到答案后的摘要和对该主题的进一步研究

事实证明,由于并行修改,我的示例的真正问题是不直接(从单个线程调用单个修改get,另一个调用{{ 1}}和valid这是安全的。)

真正的问题是wait对象的std::future函数是从不同的线程访问的,这不是预期的用例! get对象只能在一个线程中使用!

涉及的唯一其他线程是设置共享状态的线程:通过从传递给std::future的函数返回或在相关std::async对象上调用set_value等。

更多:甚至std::promise来自另一个线程的wait对象不是预期的行为(由于与我的示例#1中的UB非常相同)。我们将std::future用于此用例,让每个线程拥有自己的std::shared_future对象副本。请注意,所有这些都是通过相同的共享std::shared_future对象,而是通过单独的(相关)对象!

底线: 线程之间不应共享这些对象。在每个线程中使用单独的(相关的)对象。

2 个答案:

答案 0 :(得分:16)

正常std::future本身并不是线程安全的。所以是的,它是UB,如果你调用来自单个std::future的多个线程的函数,因为你有潜在的竞争条件。但是,从多个线程调用wait是可以的,因为它是常量/非修改。

但是,如果您确实需要从多个主题访问std::future的返回值,则可以先调用std::future::share以获取std::shared_future,然后将其复制到每个主题线程,然后每个线程可以调用get。请注意,每个线程都有自己的std::shared_future对象非常重要。

只有在某种可能的情况下您的未来可能无效时才需要检查有效,而正常使用情况(std::async等)和正确用法(例如:不是callig {{1}两次)。

答案 1 :(得分:7)

Futures允许您从一个线程存储状态并从另一个线程检索它。它们不提供任何进一步的线程安全性。

  

这真的是一种可能的未定义行为吗?

如果你有两个线程试图在没有同步的情况下获得未来的状态,是的。我不知道为什么你会这样做。

  

是否有任何符合标准的方法来避免可能的未定义行为?

只尝试从一个线程获取状态;或者,如果您确实需要在线程之间共享它,请使用互斥锁或其他同步。

  

如果我只依靠标准笔记

,这没关系

如果您知道您需要支持的唯一实施方案遵循该建议,是的。但是没有必要。

  

并且只使用f.wait()而不检查f的有效性?

如果你没有做任何有多个线程访问未来的怪异恶作剧,那么你可以假设它一直有效,直到你检索到状态(或将它移到另一个未来)。