24

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?

Guy Avraham
  • 2,830
  • 2
  • 31
  • 43
madu
  • 4,724
  • 11
  • 46
  • 79

5 Answers5

31

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.

Mike Seymour
  • 235,407
  • 25
  • 414
  • 617
  • is it garanteed thread safe if returning Singleton& instead of Singleton * ? – Romain-p Mar 04 '19 at 16:15
  • @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. – Awesome Man Jan 01 '21 at 14:02
14

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.

Pete Becker
  • 69,019
  • 6
  • 64
  • 147
3

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;
Pete Becker
  • 69,019
  • 6
  • 64
  • 147
  • 10
    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. – Xeo Sep 03 '12 at 13:29
  • @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). – Pete Becker Sep 03 '12 at 13:31
  • Well, I already wrote one, but the question isn't really a duplicate, atleast in my opinion: http://stackoverflow.com/a/11711991/500104. – Xeo Sep 03 '12 at 13:34
  • 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... – Steve Jessop Sep 03 '12 at 13:45
  • @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. – Pete Becker Sep 03 '12 at 15:49
3

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;
    }
}
sehe
  • 328,274
  • 43
  • 416
  • 565
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);
}
piokuc
  • 22,730
  • 9
  • 64
  • 94