Singleton instance declared as static variable of GetInstance method, is it thread-safe?
我见过单例模式的实现,其中实例变量在getInstance方法中声明为静态变量。这样地:
1 2 3 4 5 | SomeBaseClass &SomeClass::GetInstance() { static SomeClass instance; return instance; } |
我看到了这种方法的以下积极方面:
- 百万千克1代码更简单,因为只有在第一次调用getInstance时,编译器才负责创建这个对象。百万千克1百万千克1代码更安全,因为没有其他方法来获取对实例的引用,但是使用getInstance方法,并且没有其他方法来更改实例,而是在getInstance方法内部。百万千克1
型
这种方法的消极方面是什么(除了这不是很OOP ISH)?这条线安全吗?
在C++ 11中,它是线程安全的:
§6.7 [stmt.dcl] p4 If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization.
在C++ 03中:
- 在G++下,它是线程安全的。但这是因为G++显式地添加了代码来保证它。
一个问题是,如果你有两个单件,他们试图在建设和破坏过程中互相使用。
阅读此:查找C++静态初始化顺序问题
这个问题的一个变化是,如果从全局变量的析构函数访问单例。在这种情况下,singleton肯定已被销毁,但get方法仍将返回对已销毁对象的引用。
有很多方法可以解决这个问题,但它们很混乱,不值得做。不要从全局变量的析构函数访问单例。
一个更安全但丑陋的定义:我相信你可以添加一些适当的宏来整理这个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | SomeBaseClass &SomeClass::GetInstance() { #ifdef _WIN32 Start Critical Section Here #elif defined(__GNUC__) && (__GNUC__ > 3) // You are OK #else #error Add Critical Section for your platform #endif static SomeClass instance; #ifdef _WIN32 END Critical Section Here #endif return instance; } |
如图所示,它不是线程安全的。C++语言在线程上是无声的,所以语言没有内在的保证。您必须使用平台同步原语(例如win32::enterCriticalSection())来保护访问。
您的特定方法是有问题的b/c编译器将在第一次调用时插入一些(非线程安全的)代码来初始化静态
使用指向
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | #include <boost/shared_ptr.hpp> namespace { //Could be implemented as private member of SomeClass instead.. boost::shared_ptr<SomeClass> g_instance; } SomeBaseClass &SomeClass::GetInstance() { //Synchronize me e.g. ::EnterCriticalSection() if(g_instance == NULL) g_instance = boost::shared_ptr<SomeClass>(new SomeClass()); //Unsynchronize me e.g. :::LeaveCriticalSection(); return *g_instance; } |
我还没有编译这个,所以它只是为了说明目的。它还依赖于Boost库来获得与原始示例相同的生存期(或大致相同的生存期)。您还可以使用STD::Tr1(C++0x)。
根据规范,这也应该在VC++中工作。有人知道是不是?
只需添加关键字volatile。如果MSDN上的DOC是正确的,VisualC++编译器就应该生成互斥体。
1 2 3 4 5 | SomeBaseClass &SomeClass::GetInstance() { static volatile SomeClass instance; return instance; } |
它共享了单例实现的所有常见故障,即:
- 它不稳定
- 它不是线程安全的(这很简单,足以让您想象两个线程同时进入函数)
- 是内存泄漏
我建议不要在任何生产代码中使用singleton。