免责声明:我来自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下编译的。
这种实施不安全吗?如果是这样,怎么样?
答案 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
的修改指向。因此,线程将绕过互斥锁定并返回指向非构造对象的指针。