以下是使用延迟初始化的 Singleton
(Meyers' Singleton) 实现,这个实现线程安全吗?
static Singleton& instance()
{
static Singleton s;
return s;
}
如果不安全,为什么不安全?如何使其线程安全?以下是使用延迟初始化的 Singleton
(Meyers' Singleton) 实现,这个实现线程安全吗?
static Singleton& instance()
{
static Singleton s;
return s;
}
如果不安全,为什么不安全?如何使其线程安全?§6.7 [stmt.dcl] p4
:
如果在变量初始化时同时进入声明,则并发执行必须等待初始化完成。
GCC和VS对该功能(Dynamic Initialization and Destruction with Concurrency,也称为MSDN上的Magic Statics)的支持如下:
感谢@Mankarse和@olen_gam的评论。
instance()
必须调用Singleton s
的构造函数。要实现线程安全,这必须在临界区中发生,但标准没有要求采取临界区(至今标准完全对线程保持沉默)。编译器通常使用简单的检查和静态布尔值递增来实现此操作-但不在临界区内。类似以下伪代码:static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof( Singleton)];
if (!initialized) {
initialized = true;
new( &s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>( &s)));
}
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection( this);
}
~CritSection() {
DeleteCriticalSection( this);
}
private:
// disable copy and assignment of CritSection
CritSection( CritSection const&);
CritSection& operator=( CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton( Singleton const&);
Singleton& operator=( Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection( &instance_lock);
static Singleton s;
LeaveCriticalSection( &instance_lock);
return s;
}
哇,这是为了“让全球更好”而努力的大量工作。
如果我没有漏掉一些错误,那么这种实现的主要缺点是:
new Singleton()
抛出异常,锁将不会被释放。可以通过使用真正的RAII锁对象来修复此问题,而不是使用此处的简单锁。如果您使用类似于Boost的东西为锁提供平台独立的包装器,则还可以帮助使事情具有可移植性。main()
之后请求Singleton实例时,可以保证线程安全-如果在此之前调用它(例如在静态对象的初始化中),则可能无法正常工作,因为CRITICAL_SECTION
可能未初始化。new Singleton()
抛出异常会发生什么? - sbiInitializeCriticalSection()
应该放在哪里,用于 instance_lock
? - user3819404看下一个标准(6.7.4节),它解释了静态局部初始化是线程安全的。因此,一旦该标准的这部分广泛实现,Meyer's Singleton将成为首选实现。
我对许多答案持不同意见。大多数编译器已经以这种方式实现了静态初始化。唯一突出的例外是Microsoft Visual Studio。
正确答案取决于你的编译器。它可以决定使其线程安全;它不是“自然”线程安全。
正如MSalters所说:这取决于你使用的C++实现。请查看文档。至于另一个问题:“如果不是,为什么?”-- C++标准尚未涉及线程。但即将推出的C++版本知道线程,并明确指出静态本地变量的初始化是线程安全的。如果两个线程调用这样的函数,一个线程将执行初始化,而另一个线程将阻塞并等待其完成。