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

Nor*_*rci 15 c++ c++11

C++ 11标准说:

30.6.6类模板未来

(3)"调用除析构函数之外的任何成员函数,移动赋值运算符或对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;
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

"[注意:鼓励实现检测此情况并抛出类型为future_error的对象,错误条件为future_errc::no_state.-endnote]"

问题3:如果我只依靠标准的说明并且只是在f.wait()没有检查f有效性的情况下使用它,这样可以吗?

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

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

事实证明,我的例子的真正问题不是直接由于并行修改(get从单个线程调用单个修改,调用另一个线程valid并且wait应该是安全的).

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

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

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

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

inf*_*inf 16

法线std::future本身不是线程安全的.所以是的,它是UB,如果你在一个单独的多个线程上调用函数,std::future因为你有潜在的竞争条件.虽然,wait从多个线程调用是可以的,因为它是常量/非修改.

但是,如果您确实需要std::future从多个线程访问a的返回值,您可以先调用std::future::sharefuture来获取std::shared_future可以复制到每个线程的一个,然后每个线程都可以调用get.请注意,每个线程都有自己的std::shared_future对象很重要.

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


Mik*_*our 7

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

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

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

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

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

如果我只依靠标准的说明,那没关系

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

只是在f.wait()没有检查f有效性的情况下使用?

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

  • "只尝试从一个线程获得状态"这个.非常. (3认同)