C++ 单例实例声明为 GetInstance 方法的静态变量,它是线程安全的吗?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/449436/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Singleton instance declared as static variable of GetInstance method, is it thread-safe?
提问by okutane
I've seen implementations of Singleton patterns where instance variable was declared as static variable in GetInstance method. Like this:
我见过单例模式的实现,其中实例变量在 GetInstance 方法中被声明为静态变量。像这样:
SomeBaseClass &SomeClass::GetInstance()
{
static SomeClass instance;
return instance;
}
I see following positive sides of this approach:
我看到了这种方法的以下积极方面:
- The code is simpler, because it's compiler who responsible for creating this object only when GetInstance called for the first time.
- The code is safer, because there is no other way to get reference to instance, but with GetInstance method and there is no other way to change instance, but inside GetInstance method.
- 代码更简单,因为只有在第一次调用 GetInstance 时,才由编译器负责创建此对象。
- 代码更安全,因为没有其他方法可以获取对实例的引用,而是使用 GetInstance 方法并且没有其他方法可以更改实例,而是在 GetInstance 方法中。
What are the negative sides of this approach (except that this is not very OOP-ish) ? Is this thread-safe?
这种方法的缺点是什么(除了这不是非常面向对象编程)?这是线程安全的吗?
回答by Martin York
In C++11 it is thread safe:
在 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.
§6.7 [stmt.dcl] p4 如果在初始化变量的同时控制进入声明,并发执行将等待初始化完成。
In C++03:
在 C++03 中:
- Under g++ it is thread safe.
But this is because g++ explicitly adds code to guarantee it.
- 在 g++ 下,它是线程安全的。
但这是因为 g++ 显式添加了代码来保证它。
One problem is that if you have two singletons and they try and use each other during construction and destruction.
一个问题是,如果您有两个单身人士,并且他们在构建和销毁期间尝试互相使用。
Read this: Finding C++ static initialization order problems
阅读本文: 查找 C++ 静态初始化顺序问题
A variation on this problem is if the singleton is accessed from the destructor of a global variable. In this situation the singleton has definitely been destroyed, but the get method will still return a reference to the destroyed object.
这个问题的一个变体是如果从全局变量的析构函数访问单例。在这种情况下,单例肯定已被销毁,但 get 方法仍将返回对被销毁对象的引用。
There are ways around this but they are messy and not worth doing. Just don't access a singleton from the destructor of a global variable.
有办法解决这个问题,但它们很混乱,不值得做。只是不要从全局变量的析构函数访问单例。
A Safer definition but ugly:
I am sure you can add some appropriate macros to tidy this up
一个更安全但丑陋的定义:
我相信你可以添加一些适当的宏来整理它
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;
}
回答by Henk
It is not thread safe as shown. The C++ language is silent on threads so you have no inherent guarantees from the language. You will have to use platform synchronization primitives, e.g. Win32 ::EnterCriticalSection(), to protect access.
如图所示,它不是线程安全的。C++ 语言对线程保持沉默,因此您无法从该语言中获得内在的保证。您必须使用平台同步原语,例如 Win32 ::EnterCriticalSection(),来保护访问。
Your particular approach would be problematic b/c the compiler will insert some (non-thread safe) code to initialize the static instance
on first invocation, most likely it will be before the function body begins execution (and hence before any synchronization can be invoked.)
您的特定方法将有问题 b/c 编译器将插入一些(非线程安全)代码以instance
在第一次调用时初始化静态,很可能会在函数体开始执行之前(因此在可以调用任何同步之前)。 )
Using a global/static member pointer to SomeClass
and then initializing within a synchronized block would prove less problematic to implement.
使用全局/静态成员指针指向SomeClass
同步块,然后在同步块内初始化将证明实现的问题较小。
#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;
}
I haven't compiled this so it's for illustrative purposes only. It also relies on the boost library to obtain the same lifetime (or there about) as your original example. You can also use std::tr1 (C++0x).
我没有编译这个,所以它仅用于说明目的。它还依赖于 boost 库来获得与原始示例相同的生命周期(或大约)。您还可以使用 std::tr1 (C++0x)。
回答by Sal
According to specs this should also work in VC++. Anyone know if it does?
根据规范,这也应该适用于 VC++。有谁知道有没有?
Just add keyword volatile. The visual c++ compiler should then generate mutexes if the doc on msdn is correct.
只需添加关键字 volatile。如果 msdn 上的文档正确,则 Visual C++ 编译器应生成互斥锁。
SomeBaseClass &SomeClass::GetInstance()
{
static volatile SomeClass instance;
return instance;
}
回答by 1800 INFORMATION
It shares all of the common failings of Singleton implementations, namely:
它共享单例实现的所有常见失败,即:
- It is untestable
- It is not thread safe (this is trivial enough to see if you imagine two threads entering the function at the same time)
- It is a memory leak
- 这是不可测试的
- 它不是线程安全的(这很简单,可以查看您是否想象两个线程同时进入该函数)
- 这是内存泄漏
I recommend never using Singleton in any production code.
我建议永远不要在任何生产代码中使用 Singleton。