c++ 11中的双重检查锁定

Double-checked locking in C++11?

本文关键字:检查 锁定 c++      更新时间:2023-10-16

下面是来自http://www.ibm.com/developerworks/java/library/j-dcl/index.html

的Java示例问题
public static Singleton getInstance()
{
  if (instance == null) //#4
  {
    synchronized(Singleton.class) {  //#1
      if (instance == null)          //#2
        instance = new Singleton();  //#3
    }
  }
  return instance;
}

这似乎是不安全的,因为#3可以在构造函数执行之前将instance设置为非空,因此当另一个线程检查#4上的实例时,它将不是空的,并返回一个没有正确构造的实例。

显然,使用函数变量没有帮助,因为它可能会被优化,或者只是以一种方式执行,当我们不希望它将值设置为instance时。

我认为最简单的方法是有一个函数new Singleton();,所以它在分配给实例之前完成。现在的问题是我如何告诉c++一个函数不应该内联?我认为 Singleton* make_singleton() volatile应该这样做,但我肯定我错了。

我将暂时忽略单例的位,并假设您需要它用于惰性初始化,而不是用于像单例这样愚蠢的东西。

我建议忘记双重检查锁定。对于这种情况,c++提供了一个非常有用的工具,即std::call_once,所以请使用它。

template <typename T>
struct lazy {
public:
    // needs constraining to prevent from doing copies
    // see: http://flamingdangerzone.com/cxx11/2012/06/05/is_related.html
    template <typename Fun>
    explicit lazy(Fun&& fun) : fun(std::forward<Fun>(fun)) {}
    T& get() const {
         std::call_once(flag, [this] { ptr.reset(fun()); });
         return *ptr;
    }
    // more stuff like op* and op->, implemented in terms of get()
private:
    std::once_flag flag;
    std::unique_ptr<T> ptr;
    std::function<T*()> fun;
};
// --- usage ---
lazy<foo> x([] { return new foo; });

这正是原子的设计目的。通过将结果存储到原子中,您知道编译器不能在原子设置之后对任何关键存储或操作进行排序。原子既用于发出处理器指令原语,以确保必要的顺序一致性(例如,跨内核的缓存一致性),也用于告诉编译器必须保留哪些语义(从而限制它可以执行的重排序类型)。如果在这里使用原子,则函数是否内联并不重要,因为编译器所做的任何内联都必须保留原子本身的语义。

您可能也有兴趣研究std::call_once,这也是为这种情况而设计的,更具体地说,是针对多个线程可能需要完成某些事情的情况,但恰好其中一个应该这样做。