C++ 如何在Windows中创建线程安全的单例模式?
我一直在阅读线程安全的单例模式: 它在底部说,唯一安全的方法是使用pthread_一次,这在Windows上是不可用的 这是保证线程安全初始化的唯一方法吗 我读过这篇文章,所以: 似乎暗示了一个原子操作系统级的交换和比较功能,我认为在Windows上是: 这能满足我的要求吗 Edit:我希望延迟初始化,并且该类只有一个实例 另一个站点上有人提到在名称空间中使用全局(他将单例描述为反模式)——它怎么可能是“反模式” 接受答案:C++ 如何在Windows中创建线程安全的单例模式?,c++,windows,singleton,C++,Windows,Singleton,我一直在阅读线程安全的单例模式: 它在底部说,唯一安全的方法是使用pthread_一次,这在Windows上是不可用的 这是保证线程安全初始化的唯一方法吗 我读过这篇文章,所以: 似乎暗示了一个原子操作系统级的交换和比较功能,我认为在Windows上是: 这能满足我的要求吗 Edit:我希望延迟初始化,并且该类只有一个实例 另一个站点上有人提到在名称空间中使用全局(他将单例描述为反模式)——它怎么可能是“反模式” 接受答案: 我已经接受了,因为我正在使用Visual Studio 2008
我已经接受了,因为我正在使用Visual Studio 2008-注意:对于未来的读者,如果您没有使用此编译器(或2005)-请不要使用已接受的答案 编辑: 除了return语句之外,代码工作正常-我收到一个错误: 错误C2440:“返回”:无法从“volatile Singleton*”转换为“Singleton*”。 我是否应该将返回值修改为volatile Singleton*?
编辑:显然const\u cast将删除volatile限定符。再次感谢Josh。在windows上进行线程安全的Singleton*初始化有很多方法。事实上,其中一些甚至是跨平台的。在你链接到的SO线程中,他们正在寻找一个用C语言懒洋洋地构造的单例,这有点具体,而且考虑到你所使用的内存模型的复杂性,要正确地完成这项工作可能会有点棘手
- 你不应该用它
// A critical section guard - create on the stack to provide
// automatic locking/unlocking even in the face of uncaught exceptions
class Guard {
private:
LPCRITICAL_SECTION CriticalSection;
public:
Guard(LPCRITICAL_SECTION CS) : CriticalSection(CS) {
EnterCriticalSection(CriticalSection);
}
~Guard() {
LeaveCriticalSection(CriticalSection);
}
};
// A thread-safe singleton
class Singleton {
private:
static Singleton* Instance;
static CRITICAL_SECTION InitLock;
CRITICIAL_SECTION InstanceLock;
Singleton() {
// Time consuming initialization here ...
InitializeCriticalSection(&InstanceLock);
}
~Singleton() {
DeleteCriticalSection(&InstanceLock);
}
public:
// Not thread-safe - to be called from the main application thread
static void Create() {
InitializeCriticalSection(&InitLock);
Instance = NULL;
}
// Not thread-safe - to be called from the main application thread
static void Destroy() {
delete Instance;
DeleteCriticalSection(&InitLock);
}
// Thread-safe lazy initializer
static Singleton* GetInstance() {
Guard(&InitLock);
if (Instance == NULL) {
Instance = new Singleton;
}
return Instance;
}
// Thread-safe operation
void doThreadSafeOperation() {
Guard(&InstanceLock);
// Perform thread-safe operation
}
};
但是,完全避免使用单例是有充分理由的(以及为什么它们有时被称为反模式):
- 它们本质上是美化的全局变量
- 它们可能导致应用程序的不同部分之间的高度耦合
- 它们会使单元测试变得更加复杂或不可能(因为很难用假实现替换真正的单例)
另一种方法是使用“逻辑单例”,在主线程中创建和初始化一个类的单个实例,并将其传递给需要它的对象。如果有许多对象要创建为单例,这种方法可能会变得很笨拙。在这种情况下,可以将不同的对象捆绑到一个单例中调用“Context”对象,然后在必要时传递该对象。您可以使用操作系统原语(如互斥体或临界区)来确保线程安全初始化,但是每次访问单例指针时(由于获取锁),这将导致开销这也是不可移植的。 < P>有一个澄清点你需要考虑这个问题。你需要……/P>
网络上有很多示例来实现C++中的这些模式。这里是< p>下面解释了如何在C语言中实现它,但是完全相同的概念适用于支持单模式的任何编程语言。 您需要决定是否需要延迟初始化。延迟初始化意味着单例中包含的对象是在第一次调用它时创建的 例: 如果该调用直到稍后才进行,那么正如本文所解释的,线程之间存在争用条件的危险
MySingleton::getInstance()->initSingleton();
在代码的一开始,假设它是线程安全的,那么您就不再懒惰初始化,您将需要“一些”当你的应用程序启动时,处理能力更强。但是,如果你这样做,它将解决很多关于种族条件的问题。 < P>如果你使用Visual C++ 2005/2008,你可以使用双检查锁定模式,因为“”,这是实现一个懒惰的初始化单体的最有效的方法。 从
Singleton*GetSingleton()
{
易失性静态单态*p单态=0;
如果(pSingleton==NULL)
{
肠危重科(&cs);
如果(pSingleton==NULL)
{
尝试
{
pSingleton=新的Singleton();
}
捕获(…)
{
//出了点问题。
}
}
离开关键部门(&cs);
}
返回常数(pSingleton);
}
每当需要访问singleton时,只需调用GetSingleton()。第一次调用它时,静态指针将是init
MySingleton::getInstance()->initSingleton();
Singleton* GetSingleton()
{
volatile static Singleton* pSingleton = 0;
if (pSingleton == NULL)
{
EnterCriticalSection(&cs);
if (pSingleton == NULL)
{
try
{
pSingleton = new Singleton();
}
catch (...)
{
// Something went wrong.
}
}
LeaveCriticalSection(&cs);
}
return const_cast<Singleton*>(pSingleton);
}
struct X { };
X * get_X_Instance()
{
static X x;
return &x;
}
extern int X_singleton_helper = (get_X_instance(), 1);