共享指针的双重检查锁定

时间:2016-03-17 14:33:58

标签: c++ multithreading boost double-checked-locking

免责声明:我来自Java背景,因此,我不清楚C ++(及相关库)的内部工作是如何工作的。

我已经阅读了足够的知识,双重检查锁定是邪恶的,单个模式的正确和安全的实现需要适当的工具。

我认为以下代码可能不安全,需要编译器重新排序和未初始化对象的分配,但我不确定我是否遗漏了一些我对该语言不了解的内容。

typedef boost::shared_ptr<A> APtr;

APtr g_a;
boost::mutex g_a_mutex;
const APtr& A::instance()
{
  if (!g_a)
  {
    boost::mutex::scoped_lock lock(g_a_mutex);
    if (!g_a)
    {
      g_a = boost::make_shared<A>();
    }
  }

  return g_a;
}

我相信实际代码是在C ++ 03下编译的。

这种实施不安全吗?如果是这样,怎么样?

2 个答案:

答案 0 :(得分:2)

是的,双重检查的锁定模式在古老的语言中是不安全的。我不能比What's wrong with this fix for double checked locking?中的解释做得更好:

  

问题显然是行分配实例 - 编译器可以自由分配对象,然后将指针分配给它,或者将指针设置为它将被分配的位置,然后分配它。

如果您使用的是C ++ 11 std::shared_ptr,则可以在共享指针对象上使用atomic_load and atomic_store,这可以保证彼此之间以及使用互斥锁正确组合;但是如果你正在使用C ++ 11,你也可以使用一个动态初始化的static变量,保证它是线程安全的。

答案 1 :(得分:1)

在现代C ++中,所有这些都是绝对不必要的。除了恐龙之外,单身人士代码对于任何人都应该如此简单:

A& A::instance() {
    static A a;
    return a;
}

C ++ 11及更高版本中100%线程安全。

但是,我有一个强制性陈述:单身人士是邪恶的反模式,应该永远被禁止。

关于原始代码的线程安全性

是的,前提是代码不安全。由于读取是在互斥锁之外完成的,因此完全有可能对g_a 本身的修改是可见的,而不是对对象g_a的修改指向。因此,线程将绕过互斥锁定并返回指向非构造对象的指针。