代码之家  ›  专栏  ›  技术社区  ›  okutane

singleton实例声明为getInstance方法的静态变量,它是线程安全的吗?[复制品]

  •  28
  • okutane  · 技术社区  · 15 年前

    我见过单例模式的实现,其中实例变量在getInstance方法中声明为静态变量。这样地:

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

    我看到了这种方法的以下积极方面:

    • 代码更简单,因为只有在第一次调用getInstance时,编译器才负责创建这个对象。
    • 代码更安全,因为没有其他方法来获取对实例的引用,但是使用getInstance方法,并且没有其他方法来更改实例,而是在getInstance方法内部。

    这种方法的消极方面是什么(除了这不是很OOP ISH)?这条线安全吗?

    4 回复  |  直到 6 年前
        1
  •  35
  •   Community Egal    7 年前

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

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

    在C++ 03中:

    • 在G++下,它是线程安全的。
      但这是因为G++显式地添加了代码来保证它。

    一个问题是,如果你有两个单件,他们试图在建设和破坏过程中互相使用。

    阅读此: Finding C++ static initialization order problems

    这个问题的一个变化是,如果从全局变量的析构函数访问单例。在这种情况下,singleton肯定已被销毁,但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;
    }
    
        2
  •  5
  •   Henk    15 年前

    如图所示,它不是线程安全的。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)。

        3
  •  1
  •   sra Jon    12 年前

    根据规范,这也适用于VC++。有人知道是不是?

    只需添加关键字volatile。 如果MSDN上的DOC是正确的,VisualC++编译器就应该生成互斥体。

    SomeBaseClass &SomeClass::GetInstance()
    {
       static volatile SomeClass instance;
       return instance;
    }
    
        4
  •  -7
  •   1800 INFORMATION    15 年前

    它共享了单例实现的所有常见故障,即:

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

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