如何使用双重检查锁来初始化shared_ptr

时间:2012-09-22 18:32:31

标签: c++ c++11 locking shared-ptr lazy-initialization

(假设VC ++ 2010:(1)可以使用/ volatile:ms,(2)还没有std :: atomic,(3)没有线程安全的静态变量初始化,(4)没有std :: call_once)

如果我有一个普通的C指针,我可以推出下面的双重检查锁模式,以避免每次锁定的成本:

static volatile void * ptr = nullptr;

//...
if ( ptr == nullptr)
{
   // Acquire Lock
   if (ptr == nullptr)
   {
      // some code
      // ptr = ...; // init ptr
   }
   // Release Lock
}
// ....

自VC ++ 2005以来,volatile确保上述代码正确无误。假设我没有可移植的代码。

现在假设我需要用std :: shared_ptr或boost :: shared_ptr替换普通指针,我该如何做同样的事情?如何使shared_ptr易变?我需要另一个易变的旗帜吗?

3 个答案:

答案 0 :(得分:4)

使用C ++ 11,shared_ptr有原子访问函数。要编写使用shared_ptr的双重检查锁,请使用这些访问者:

static std::shared_ptr<MyType> ptr;
if (std::atomic_load(ptr) == 0) {
    // lock the lock
    if (std::atomic_load(ptr) == 0) {
        std::shared_ptr<MyType> local_ptr(new MyType);
        std::atomic_store(ptr, local_ptr);
    }
    // unlock the lock
}
return ptr;

答案 1 :(得分:3)

  

自VC ++ 2005以来,volatile确保上述代码是正确的。

不,它没有。 volatile与线程或原子性无关。

您当前的代码不正确,任何C ++标准都无法保证产生合理的行为。

由于你的假装锁定代码一般不起作用,它肯定不适用于shared_ptr或其他智能指针。如果你想要更便宜的锁定,请研究无锁编码模式。

答案 2 :(得分:1)

在C ++ 2011中,使用任何显式同步甚至不是必需的。根据6.7 [stmt.dcl]第4段,初始化由系统同步:

  

如果控件在初始化变量时同时进入声明,则并发执行应等待初始化完成。

这似乎暗示std::shared_ptr<T>可以这样初始化:

{
    static std::shared_ptr<MyType> ptr(new MyType(/*...*/));
    // ...
}