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

How to use double checked lock to init a shared_ptr

本文关键字:初始化 shared ptr 何使用 检查      更新时间:2023-10-16

>(假设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 开始,易失性确保上述代码是正确的。假设我对代码不可移植感到满意。

现在假设我需要用 std::shared_ptr 或 boost::shared_ptr 替换普通指针,我将如何做同样的事情?如何使这种shared_ptr波动?我是否需要另一个易失性标志?

在 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;

从 VC++ 2005 开始,易失性确保上述代码是正确的。

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

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

由于您的假装锁定代码通常不起作用,因此它肯定不适用于shared_ptr或其他智能指针。如果您想要更便宜的锁定,请查看无锁定编码模式。

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

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

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

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