Singleton with multithreads

28,800

Solution 1

In C++11, the following is guaranteed to perform thread-safe initialisation:

static Singleton* getSingletonInstance()
{
    static Singleton instance;
    return &instance;
}

In C++03, a common approach was to use double-checked locking; checking a flag (or the pointer itself) to see if the object might be uninitialised, and only locking the mutex if it might be. This requires some kind of non-standard way of atomically reading the pointer (or an associated boolean flag); many implementations incorrectly use a plain pointer or bool, with no guarantee that changes on one processor are visible on others. The code might look something like this, although I've almost certainly got something wrong:

static Singleton* getSingletonInstance()
{
    if (!atomic_read(singletonInstance)) {
        mutex_lock lock(mutex);
        if (!atomic_read(singletonInstance)) {
            atomic_write(singletonInstance, new Singleton);
        }
    }
    return singletonInstance;
}

This is quite tricky to get right, so I suggest that you don't bother. In C++11, you could use standard atomic and mutex types, if for some reason you want to keep the dynamic allocation of you example.

Note that I'm only talking about synchronised initialisation, not synchronised access to the object (which your version provides by locking the mutex in the accessor, and releasing it later via a separate function). If you need the lock to safely access the object itself, then you obviously can't avoid locking on every access.

Solution 2

As @piokuc suggested, you can also use a once function here. If you have C++11:

#include <mutex>

static void init_singleton() {
    singletonInstance = new Singleton;
}
static std::once_flag singleton_flag;

Singleton* getSingletonInstance() {
    std::call_once(singleton_flag, init_singleton);
    return singletonInstance;
}

And, yes, this will work sensibly if the new Singleton throws an exception.

Solution 3

If you use POSIX threads you can use pthread_once_t and pthread_key_t stuff, this way you can avoid using mutexes altogether. For example:

template<class T> class ThreadSingleton : private NonCopyable {
public:
    ThreadSingleton();
    ~ThreadSingleton();

    static T& instance();

private:
    ThreadSingleton( const ThreadSingleton& );
    const ThreadSingleton& operator=( const ThreadSingleton& )

    static pthread_once_t once_;
    static pthread_key_t  key_;

    static void init(void);
    static void cleanUp(void*);
};

And implementation:

template<class T> pthread_once_t ThreadSingleton<T>::once_ = PTHREAD_ONCE_INIT;
template<class T> pthread_key_t ThreadSingleton<T>::key_;

template<class T>  
T& ThreadSingleton<T>::instance()
{
    pthread_once(&once_,init);

    T* value = (T*)pthread_getspecific(key_);
    if(!value)
    {   

        value = new T();
        pthread_setspecific(key_,value);
    }   
    return *value;
}

template<class T> void ThreadSingleton<T>::cleanUp(void* data)
{
    delete (T*)data;
    pthread_setspecific(key_,0);
}

template<class T> void ThreadSingleton<T>::init()
{
    pthread_key_create(&key_,cleanUp);
}

Solution 4

If you have C++11 you can make singletonInstance an atomic variable, then use a double-checked lock:

if (singletonInstance == NULL) {
    lock the mutex
    if (singletonInstance == NULL) {
        singletonInstance = new Singleton;
    }
    unlock the mutex
}
return singletonInstance;

Solution 5

You should actually lock the singleton, and not the instance. If the instance requires locking, that should be handled by the caller (or perhaps by the instance itself, depending on what kind of an interface it exposes)

Update sample code:

#include <mutex>

class Singleton 
{
    static Singleton *singletonInstance;
    Singleton() {}
    static std::mutex m_;

  public:

    static Singleton* getSingletonInstance()
    {
        std::lock_guard<std::mutex> lock(m_);
        if(singletonInstance == nullptr)
        {
            singletonInstance = new Singleton();
        }
        return singletonInstance;
    }
}
Share:
28,800

Related videos on Youtube

madu
Author by

madu

Updated on November 28, 2020

Comments

  • madu
    madu over 3 years

    This question was asked in an interview. The first part was to write the singleton class:

    class Singleton
    {
        static Singleton *singletonInstance;
        Singleton() {}
    
      public:
        static Singleton* getSingletonInstance()
        {
            if(singletonInstance == null)
            {
                singletonInstance = new Singleton();
            }
            return singletonInstance;
        }
    };
    

    Then I was asked how to handle this getSingletonInstance() in a multithreaded situation. I wasn't really sure, but I modified as:

    class Singleton 
    {
        static Singleton *singletonInstance;
        Singleton() {}
        static mutex m_;
    
      public:
        static Singleton* getSingletonInstance()
        {
            m_pend();
            if(singletonInstance == null)
            {
                singletonInstance = new Singleton();
            }
            return singletonInstance;
        }
    
        static void releaseSingleton()
        {
            m_post();
        }
    };
    

    Then I was told that although a mutex is required, pending and posting a mutex is not efficient as it takes time. And there is a better way to handle to this situation.

    Does anybody know a better and more efficient way to handle the singleton class in a multithreaded situation?

    • Bartek Banachewicz
      Bartek Banachewicz over 11 years
      Don't use singleton at all?
    • Cat Plus Plus
      Cat Plus Plus over 11 years
      Using global state in a multithreaded code is a nice way to get infinite headaches. Doubly so in case of stupidgleton.
    • Emile Cormier
      Emile Cormier over 11 years
      @CatPlusPlus : It's simpleton. Simpleton.
  • Xeo
    Xeo over 11 years
    If your compiler correctly implements C++11, you don't need to lock it at all. static Foo& getSingleton(){ static Foo foo{}; return foo; } will just work under concurrent execution.
  • Pete Becker
    Pete Becker over 11 years
    @Xeo - good point. Make it an answer and I'll give it an up vote, but with a comment that this may be inefficient, depending on how the compiler manages locks for function static objects (if there's one shared lock, you could get contention).
  • Xeo
    Xeo over 11 years
    Well, I already wrote one, but the question isn't really a duplicate, atleast in my opinion: stackoverflow.com/a/11711991/500104.
  • Steve Jessop
    Steve Jessop over 11 years
    If you're going to explicitly write a line of pseudo-code for "unlock the mutex" then should the pseudo-code also ensure that it is unlocked when the new expression throws? Or can that go without saying and/or the exception be assumed to cause program termination? In a real program you'd have to decide which, so really I suppose I'm talking about pseudo-code etiquette here...
  • Mike Seymour
    Mike Seymour over 11 years
    I think the question is about creating a single instance, not one per thread.
  • piokuc
    piokuc over 11 years
    That's not how I understood the question.. I just read it again and I think you're right. Sorry.
  • Pete Becker
    Pete Becker over 11 years
    @SteveJessop - you're right in your implication that the unlock must be done if the allocation throws an exception. I was only trying to avoid calls to m_pend() and m_post(), as in the original code, since I have no idea what they do. Of course, in C++11 you'd use a lock object whose destructor unlocked the mutex.
  • madu
    madu over 11 years
    Thank you. I wasn't aware of the once function.
  • Romain-p
    Romain-p about 5 years
    is it garanteed thread safe if returning Singleton& instead of Singleton * ?
  • Yogesh
    Yogesh almost 5 years
    @PeteBecker this is the best answer, perfect use of std ns.
  • Yogesh
    Yogesh almost 5 years
    @PeteBecker getSingletonInstance() is non static, which is a problem for singleton pattern, you will have to make it static.
  • Pete Becker
    Pete Becker almost 5 years
    @Yogesh — in the code I wrote, getSingletonInstance is not a member of a class. It is not, and should not be, static.
  • Yogesh
    Yogesh almost 5 years
    @PeteBecker oh, my bad, was biased for cpp.
  • Awesome Man
    Awesome Man over 3 years
    @Romain-p, provided example is called Meyers singleton. Thread safety is achieved by relying on a C++ standard that states: "static variables initialization should be thread-safe". In other words, thread-safe initialization of static variable it is a compiler headache.