我知道以下是在 C++11 中实现单例的线程安全方法:
Foo* getInst()
{
static Foo* inst = new Foo(...);
return inst;
}
我在这个答案中读到以下内容也是线程安全的:
Foo& getInst()
{
static Foo inst(...);
return inst;
}
它真的是线程安全的吗?
在单个堆栈帧中分配的 Foo 实例不会在堆上分配,这不是一个问题吗?
如果它是线程安全的,是否有充分的理由更喜欢一个而不是另一个?
静态变量不会在堆栈上分配。在第一个变体中,你有一个静态指针(一个全局变量),用从堆获得的内存初始化,而在第二个变体中,你有整个静态对象。
这两个版本都使用内部编译器保护(即__cxa_guard_acquire()
和__cxa_guard_release()
,它们在功能上等同于mutex::lock()
和mutex::unlock()
)来确保对特殊变量的序列化访问,该变量告诉您的全局实例是否已初始化。
您的代码:
Foo& getInst()
{
static Foo inst(...);
return inst;
}
编译后实际上看起来像这样:
Foo& getInst()
{
static Foo inst; // uninitialized - zero
static guard instGuard; // zero
if (is_initialized(instGuard) == false)
{
__cxa_guard_acquire(instGuard);
// do the initialization here - calls Foo constructor
set_initialized(instGuard);
__cxa_guard_release(instGuard);
}
return inst;
}
所以你的两个考试都是线程安全的。
示例中的inst
不会在堆栈上分配。它将.data
或.bss
部分中的某个位置分配。否则,这个静态变量不能一直存在于程序执行的时间,因此它不能具有相同的值,就像以前一样,每次你输入这个函数。