关于c ++:Singleton实例声明为GetInstance方法的静态变量,它是否是线程安全的?

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编译器将在第一次调用时插入一些(非线程安全的)代码来初始化静态instance,很可能是在函数体开始执行之前(因此在调用任何同步之前)。

使用指向SomeClass的全局/静态成员指针,然后在同步块内初始化,将证明实现的问题更少。

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。