繁体   English   中英

std :: call_once和内存重新排序

[英]std::call_once and memory reordering

鉴于此处的代码:

class lazy_init
{
    mutable std::once_flag flag;
    mutable std::unique_ptr<expensive_data> data;

    void do_init() const
    {
        data.reset(new expensive_data);
    }
public:
    expensive_data const& get_data() const
    {
        std::call_once(flag,&lazy_init::do_init,this);
        return *data;
    }
};

我在其他地方也看到了相同模式的一些变体。 所以我的问题是:为什么这段代码被认为是保存? 为什么编译器在调用std :: call_once之前不能只读取数据并最终得到不正确的数据? 例如

tmp = data.get();
std::call_once(flag,&lazy_init::do_init,this);
return *tmp;

我的意思是我没有找到任何可以阻止这种情况的障碍。

如果允许编译器生成与您描述的匹配的代码,那么用C ++编程基本上是不可能的。

这在§1.9/ 14 程序执行 (n3290)中说明:

在与要评估的下一个全表达式相关联的每个值计算和副作用之前,对与全表达式相关联的每个值计算和副作用进行排序。

您的return语句在前一个完整表达式之后排序。 编译器必须输出代码,就好像在评估return语句之前已完全评估了前一个语句的所有副作用。
您的示例不遵守该规则,因为它在考虑std::call_once(...)完整表达式的副作用之前评估*data

另外, std::call_once在其描述中有这个(§30.4.4.2/ 2和3):

2 / 效果 :不调用其func的call_once的执行是被动执行。 调用其func的call_once的执行是一个活动执行。 活动执行应调用INVOKE (DECAY_- COPY ( std::forward<Callable>(func)), DECAY_COPY (std::forward<Args>(args))...) 如果对func的这种调用抛出异常,则执行异常,否则返回。 异常执行应将异常传播给call_once的调用者。 在任何给定的once_flag的call_once的所有执行中:最多一个应该是返回执行; 如果有返回执行,则应该是最后一次执行; 只有在返回执行时才会执行被动执行。 [ 注意:被动执行允许其他线程可靠地观察先前返回执行产生的结果。 - 结束说明 ]

3 / 同步 :对于任何给定的once_flag:所有活动执行都按总顺序发生; 活动执行的完成与此总订单中下一个的开始同步; 并且返回的执行与所有被动执行的返回同步。

因此标准要求同步以适合您的用例。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM