在C++ 11中进行双重检查锁定?

7 c++ concurrency race-condition c++11

以下是来自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;
}
Run Code Online (Sandbox Code Playgroud)

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

显然使用函数变量不会有帮助,因为它可能被优化或者只是在我们不希望它时将值设置为实例的方式执行.

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

R. *_*des 25

我会暂时忽略单例位并假设你需要这个用于延迟初始化而不是像单身人士那样愚蠢的东西.

我建议忘记双重检查锁定.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; });
Run Code Online (Sandbox Code Playgroud)

  • 呃.不,一个人应该更喜欢任何实现目标的人.我想调用`std :: call_once`并且只调用*,而不是其他*,使`std ::`绝对*至关重要*来确保. (23认同)
  • @ AdamH.Peterson不合格调用`swap`的情况与标准库中的其他函数完全不同.`swap`可以是用户定义的类接口的一部分,通用代码应该找到它,否则回退到`std :: swap`.但这几乎从不适用于标准库中的任何其他算法,并且应始终使用`std ::`调用它们.特别是如果你是一个库作者,不使用`std ::`会引入意想不到的自定义点(如果用户定义具有相同名称的函数,你的代码将会巧妙地破解). (8认同)