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
对象不,而是通过单独的(相关)对象!
底线: 线程之间不应共享这些对象。在每个线程中使用单独的(相关的)对象。
答案 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
的有效性?
如果你没有做任何有多个线程访问未来的怪异恶作剧,那么你可以假设它一直有效,直到你检索到状态(或将它移到另一个未来)。