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

Ank*_*kur 129 c++ multithreading design-patterns

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

static Singleton& instance()
{
     static Singleton s;
     return s;
}
Run Code Online (Sandbox Code Playgroud)

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

Gro*_*roo 154

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

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

GCC和VS支持该功能(动态初始化和销毁​​并发,在MSDN上也称为Magic Statics)如下:

感谢@Mankarse和@olen_gam的评论.


C++ 03中,此代码不是线程安全的.Meyers有一篇文章称为"C++和双重检查锁定的危险",它讨论了模式的线程安全实现,结论或多或少是(在C++ 03中)完全锁定实例化方法基本上是确保所有平台上的正确并发的最简单方法,而大多数形式的双重检查锁定模式变体可能会遇到某些体系结构上的竞争条件,除非指令与策略性交错存储内存障碍.

  • Alexandrescu在Modern C++ Design中对Singleton模式(生命周期和线程安全性)进行了广泛的讨论.参见Loki的网站:http://loki-lib.sourceforge.net/index.php?n = Pattern.Singleton (3认同)

Mic*_*urr 19

要回答你关于为什么它不是线程安全的问题,这不是因为第一次调用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)));
}
Run Code Online (Sandbox Code Playgroud)

所以这是一个简单的线程安全的Singleton(适用于Windows).它为Windows CRITICAL_SECTION对象使用一个简单的类包装器,这样我们就可以让编译器自动初始化CRITICAL_SECTION之前main()调用它.理想情况下,将使用真正的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;
}
Run Code Online (Sandbox Code Playgroud)

男人 - 这是为了"让全球变得更好".

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

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

  • 哦哦。如果 `new Singleton()` 抛出会发生什么? (2认同)

def*_*ode 9

查看下一个标准(第6.7.4节),它探讨了静态本地初始化如何是线程安全的.因此,一旦该部分标准得到广泛实施,Meyer的Singleton将成为首选实施方案.

我已经不同意许多答案了.大多数编译器已经以这种方式实现静态初始化 一个值得注意的例外是Microsoft Visual Studio.


MSa*_*ers 6

正确答案取决于您的编译器.它可以决定使其线程安全; 它不是"天生的"线程安全.


sbi*_*sbi 5

以下实现线程安全吗?

在大多数平台上,这不是线程安全的.(附加通常的免责声明,解释C++标准不知道线程,因此,从法律上讲,它不会说是否是.)

如果没有,为什么[...]?

它不是因为没有什么能阻止多个线程同时执行s'构造函数.

如何使其线程安全?

Scott Meyers和Andrei Alexandrescu 撰写的"C++和双重锁定的危险"是一篇关于线程安全单例主题的非常好的论文.