Meyers的Singleton模式线程是否安全?

以下是Singleton (Meyers'Singleton)线程安全的使用延迟初始化的实现吗?

static Singleton& instance()
{
     static Singleton s;
     return s;
}

如果没有,为什么以及如何使它线程安全?


在C ++ 11中,它是线程安全的。 根据标准, §6.7 [stmt.dcl] p4

如果控制在变量初始化时同时进入声明,则并发执行应等待初始化完成。

GCC和VS支持该功能(动态初始化和破坏并发,也称为MSDN上的Magic Statics)如下所示:

  • Visual Studio:自Visual Studio 2015以来受支持
  • GCC:自GCC 4.3以来支持
  • 感谢@Mankarse和@olen_gam的评论。


    在C ++ 03中,这段代码不是线程安全的。 Meyers有一篇名为“C ++和双重检查锁定的危险”的文章,讨论了模式的线程安全实现,并且或多或少的结论是(在C ++ 03中)完全锁定实例化方法基本上是确保在所有平台上正确并发的最简单方法,而大多数形式的双重检查锁定模式变体可能会遭受某些体系结构上的竞争条件的影响,除非指令以战略方式交织内存屏障。


    要回答你为什么不是线程安全的问题,这不是因为第一次调用instance()必须调用Singleton s构造函数。 要成为线程安全的,这必须发生在关键部分,但是在标准中没有要求采用关键部分(迄今为止的标准在线程上完全没有提示)。 编译器通常使用一个简单的检查和增量的静态布尔实现 - 但不是在关键部分。 像下面的伪代码:

    static Singleton& instance()
    {
        static bool initialized = false;
        static char s[sizeof( Singleton)];
    
        if (!initialized) {
            initialized = true;
    
            new( &s) Singleton(); // call placement new on s to construct it
        }
    
        return (*(reinterpret_cast<Singleton*>( &s)));
    }
    

    所以这里有一个简单的线程安全Singleton(对于Windows)。 它使用Windows CRITICAL_SECTION对象的简单类包装器,以便我们可以让编译器在调用main()之前自动初始化CRITICAL_SECTION 。 理想情况下,将使用一个真正的RAII关键部分类来处理在关键部分被关闭时可能发生的异常,但这超出了本答案的范围。

    基本操作是当请求一个Singleton实例时,会进行锁定,如果需要锁定Singleton,则释放该锁并返回Singleton引用。

    #include <windows.h>
    
    class CritSection : public CRITICAL_SECTION
    {
    public:
        CritSection() {
            InitializeCriticalSection( this);
        }
    
        ~CritSection() {
            DeleteCriticalSection( this);
        }
    
    private:
        // disable copy and assignment of CritSection
        CritSection( CritSection const&);
        CritSection& operator=( CritSection const&);
    };
    
    
    class Singleton
    {
    public:
        static Singleton& instance();
    
    private:
        // don't allow public construct/destruct
        Singleton();
        ~Singleton();
        // disable copy & assignment
        Singleton( Singleton const&);
        Singleton& operator=( Singleton const&);
    
        static CritSection instance_lock;
    };
    
    CritSection Singleton::instance_lock; // definition for Singleton's lock
                                          //  it's initialized before main() is called
    
    
    Singleton::Singleton()
    {
    }
    
    
    Singleton& Singleton::instance()
    {
        // check to see if we need to create the Singleton
        EnterCriticalSection( &instance_lock);
        static Singleton s;
        LeaveCriticalSection( &instance_lock);
    
        return s;
    }
    

    男人 - 这是一个“让更好的全球化”的废话。

    这个实现的主要缺点(如果我没有让一些错误通过)是:

  • 如果new Singleton()抛出,锁将不会被释放。 这可以通过使用真正的RAII锁定对象而不是我在这里的简单锁定对象来解决。 如果你使用类似Boost的方法来为锁提供一个平台独立的包装,这也可以使事物变得可移植。
  • 这在调用main()之后请求Singleton实例时保证了线程的安全性 - 如果在此之前调用它(如在静态对象的初始化中那样),事情可能不起作用,因为CRITICAL_SECTION可能未初始化。
  • 每次请求实例时都必须执行锁定。 正如我所说的,这是一个简单的线程安全实现。 如果你需要一个更好的(或者想知道为什么像双重检查锁技术是有缺陷的),请参阅Groo答案中链接的论文。

  • 看下一个标准(第6.7.4节),它展示了静态本地初始化是如何线程安全的。 所以一旦这部分标准得到广泛实施,Meyer的Singleton将成为首选实施方案。

    我不同意许多答案。 大多数编译器已经这样实现静态初始化。 唯一明显的例外是Microsoft Visual Studio。

    链接地址: http://www.djcxy.com/p/78845.html

    上一篇: Is Meyers' implementation of the Singleton pattern thread safe?

    下一篇: Finding C++ static initialization order problems