Singleton实例声明为GetInstance方法的静态变量,它是否是线程安全的? [重复]

问题描述 投票:28回答:4

这个问题在这里已有答案:

我已经看到了Singleton模式的实现,其中实例变量在GetInstance方法中被声明为静态变量。像这样:

SomeBaseClass &SomeClass::GetInstance()
{
   static SomeClass instance;
   return instance;
}

我认为这种方法有以下积极方面:

  • 代码更简单,因为它的编译器只负责在第一次调用GetInstance时创建此对象。
  • 代码更安全,因为没有其他方法可以获取对实例的引用,但是使用GetInstance方法并没有其他方法来更改实例,而是在GetInstance方法中。

这种方法的不利方面是什么(除了这不是非常OOP-ish)?这是线程安全的吗?

c++ design-patterns singleton
4个回答
35
投票

在C ++ 11中,它是线程安全的:

§6.7[stmt.dcl] p4如果控制在初始化变量时同时进入声明,则并发执行应等待初始化完成。

在C ++ 03中:

  • 在g ++下它是线程安全的。 但这是因为g ++明确地添加了代码来保证它。

一个问题是,如果你有两个单身人士,他们会在施工和破坏期间互相尝试使用。

阅读本文:Finding C++ static initialization order problems

这个问题的一个变种是如果从全局变量的析构函数访问单例。在这种情况下,单例肯定已被破坏,但get方法仍将返回对被破坏对象的引用。

有办法解决这个问题,但它们很混乱,不值得做。只是不要从全局变量的析构函数中访问单例。

一个更安全的定义,但丑陋: 我相信你可以添加一些合适的宏来整理它

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;
}

5
投票

如图所示,它不是线程安全的。 C ++语言在线程上是静默的,因此您没有语言的固有保证。您将不得不使用平台同步原语,例如Win32 :: EnterCriticalSection(),用于保护访问。

您的特定方法会有问题b / c编译器将在第一次调用时插入一些(非线程安全)代码来初始化静态instance,很可能它将在函数体开始执行之前(因此在调用任何同步之前) 。)

使用指向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;
}

我没有编译这个,所以它仅用于说明目的。它还依赖于boost库来获得与原始示例相同的生命周期(或那里)。您还可以使用std :: tr1(C ++ 0x)。


1
投票

根据规范,这也适用于VC ++。任何人都知道它是否存在?

只需添加关键字volatile。如果msdn上的doc是正确的,那么visual c ++编译器应该生成互斥锁。

SomeBaseClass &SomeClass::GetInstance()
{
   static volatile SomeClass instance;
   return instance;
}

-7
投票

它分享了Singleton实现的所有常见缺陷,即:

  • It is untestable
  • 它不是线程安全的(这很简单,看你是否想象两个线程同时进入函数)
  • 这是一个内存泄漏

我建议不要在任何生产代码中使用Singleton。

© www.soinside.com 2019 - 2024. All rights reserved.