Is Meyers' implementation of the Singleton pattern thread safe?

前端 未结 6 832
我寻月下人不归
我寻月下人不归 2020-11-22 04:55

Is the following implementation, using lazy initialization, of Singleton (Meyers\' Singleton) thread safe?

static Singleton& instance()
{
           


        
相关标签:
6条回答
  • 2020-11-22 05:07

    The correct answer depends on your compiler. It can decide to make it threadsafe; it's not "naturallly" threadsafe.

    0 讨论(0)
  • 2020-11-22 05:08

    To answer your question about why it's not threadsafe, it's not because the first call to instance() must call the constructor for Singleton s. To be threadsafe this would have to occur in a critical section, and but there's no requirement in the standard that a critical section be taken (the standard to date is completely silent on threads). Compilers often implement this using a simple check and increment of a static boolean - but not in a critical section. Something like the following pseudocode:

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

    So here's a simple thread-safe Singleton (for Windows). It uses a simple class wrapper for the Windows CRITICAL_SECTION object so that we can have the compiler automatically initialize the CRITICAL_SECTION before main() is called. Ideally a true RAII critical section class would be used that can deal with exceptions that might occur when the critical section is held, but that's beyond the scope of this answer.

    The fundamental operation is that when an instance of Singleton is requested, a lock is taken, the Singleton is created if it needs to be, then the lock is released and the Singleton reference returned.

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

    Man - that's a lot of crap to "make a better global".

    The main drawbacks to this implemention (if I didn't let some bugs slip through) is:

    • if new Singleton() throws, the lock won't be released. This can be fixed by using a true RAII lock object instead of the simple one I have here. This can also help make things portable if you use something like Boost to provide a platform independent wrapper for the lock.
    • this guarantees thread safety when the Singleton instance is requested after main() is called - if you call it before then (like in a static object's initialization) things might not work because the CRITICAL_SECTION might not be initialized.
    • a lock must be taken each time an instance is requested. As I said, this is a simple thread safe implementation. If you need a better one (or want to know why things like the double-check lock technique is flawed), see the papers linked to in Groo's answer.
    0 讨论(0)
  • 2020-11-22 05:14

    In C++11, it is thread safe. According to the standard, §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.

    GCC and VS support for the feature (Dynamic Initialization and Destruction with Concurrency, also known as Magic Statics on MSDN) is as follows:

    • Visual Studio: supported since Visual Studio 2015
    • GCC: supported since GCC 4.3

    Thanks to @Mankarse and @olen_gam for their comments.


    In C++03, this code wasn't thread safe. There is an article by Meyers called "C++ and the Perils of Double-Checked Locking" which discusses thread safe implementations of the pattern, and the conclusion is, more or less, that (in C++03) full locking around the instantiating method is basically the simplest way to ensure proper concurrency on all platforms, while most forms of double-checked locking pattern variants may suffer from race conditions on certain architectures, unless instructions are interleaved with strategically places memory barriers.

    0 讨论(0)
  • 2020-11-22 05:23

    As MSalters said: It depends on the C++ implementation you use. Check the documentation. As for the other question: "If not, why?" -- The C++ standard doesn't yet mention anything about threads. But the upcoming C++ version is aware of threads and it explicitly states that the initialization of static locals is thread-safe. If two threads call such a function, one thread will perform an initialization while the other will block & wait for it to finish.

    0 讨论(0)
  • 2020-11-22 05:26

    Is the following implementation [...] thread safe?

    On most platforms, this is not thread-safe. (Append the usual disclaimer explaining that the C++ standard doesn't know about threads, so, legally, it doesn't say whether it is or not.)

    If not, why [...]?

    The reason it isn't is that nothing prevents more than one thread from simultaneously executing s' constructor.

    how to make it thread safe?

    "C++ and the Perils of Double-Checked Locking" by Scott Meyers and Andrei Alexandrescu is a pretty good treatise on the subject of thread-safe singletons.

    0 讨论(0)
  • 2020-11-22 05:28

    Looking at the next standard (section 6.7.4), it explians how static local initialization is thread safe. So once that section of standard is widely implemented, Meyer's Singleton will be the preferred implementation.

    I disagree with many answers already. Most compilers already implement static initialization this way. The one notable exception is Microsoft Visual Studio.

    0 讨论(0)
提交回复
热议问题