Design Singleton class for multi-threaded environment in C++

(Last Updated On: June 20, 2016)

Answer: For c++ thread safe singleton class design for multi threaded environment, we need to synchronize the function that creates and returns singleton object.

We will see the singleton class design without synchronization and with synchronization applying on function that create singleton object in C++

For example, in the below design “GetInstance()”  function is not thread safe.

As multiple threads can enter into the “if(NULL == s_instance)” block and can create multiple singleton instances.

 

So, to have unique instance across the application in multi-threaded environment, we need to put some lock above and below of conditional block, so only one thread can enter into it and create object. Once, one thread has created an object other thread will not be allowed.

First, let’s test if below c++ singleton class design needs synchronization.

//This design is not thread safe 
class Singleton{
public:
	//this function creates Singleton object and return it to users.

	//This design is not thread safe
    static Singleton* GetInstance(){

			if(NULL == s_instance)//Inside this condition multiple threads can enter and create objects
			{
				 s_instance=new Singleton();
			}
		return s_instance;      
	}
   ~Singleton(){}	
private:
	Singleton(){cout<<"constructor...\n";}
	static Singleton* s_instance;	
    
};
//initiaize static variables here
Singleton *Singleton::s_instance = 0;

We know that for singleton class only one object will be created. So, lets, test the above design with below test code and see if we can get the same address of objects on different call of GetInstance() function in multi-threaded application.

Here is the output for the below test sample. Notice that both addresses are different, means, both main thread and child thread have created different objects that violets singleton class design.

Output:

constructor…
Address of object in main thread:006981B0
constructor…
Address of object in thread:006981E0
Thread handle closed.

Note: you may not always get different addresses but same and you need to run test code multiple times.

Test sample:

 

/*---------------------------------------------------------------
* Sample to test singleton class into multi threaded application
*/
#include 
#include 
using namespace std;

DWORD WINAPI threadFunction(LPVOID lpParam)
{
	//Get object of singleton class into a thread
	Singleton *ob2=Singleton::GetInstance();
	cout<<"Address of object in thread:"<<ob2<<endl;
	
	return 0;
}


int main(){
 
	HANDLE hThread;
	DWORD ThreadId;

	hThread = CreateThread(
		NULL,				// default security attributes
		0,					// use default stack size 
		threadFunction,		// thread function
		NULL,		// argument to thread function
		0,					// use default creation flags
		&ThreadId);		// returns the thread identifier

 

	if (hThread == NULL){
		printf("CreateThread() failed, error: %d.\n", GetLastError());
		return 1;
	}

	//Get object of singleton class into main thread
	Singleton *ob1 = Singleton::GetInstance();
	cout<<"Address of object in main thread:"<<ob1<<endl;
	
	WaitForSingleObject(hThread,INFINITE);//wait for thread to finish.
	

	if (CloseHandle(hThread) != 0){
		 printf("Thread handle closed.\n"); 
	}
	
	return 0;
}

 

C++ thread safe Singleton class design

Note that we have put two if(NULL == s_instance)condition. One condition prevent execution of _lock.acquire()and _lock.release()on multiple GetInstance() method call. And second condition, will prevent multiple thread to enter into the block and to creat multiple objects.

//This design thread safe //singleton.cpp
#include 
#include "lock.h"
#include 
using namespace std;

static Lock _lock;
//This design thread safe 
class Singleton{
public:
	//this function creates Singleton object and return it to users.	
	static Singleton* GetInstance(){

		// putting a pre condition to avoid locks on multiple calls
		// if we don't put this condition,_lock.acquire()and _lock.release() will always be executed on 
		// multiple GetInstance() call and will affect performance.
		if(NULL == s_instance)
		{
			_lock.acquire();

			if(NULL == s_instance)
			{
				s_instance=new Singleton();
			}
			_lock.release();

		}

		return s_instance;      
	}  


	~Singleton(){}	
private:
	Singleton(){cout<<"constructor...\n";}
	static Singleton* s_instance;	

};
//initiaize static variables here
Singleton *Singleton::s_instance = 0;

//Lock class lock.h

#include 
 class Lock
    {
    public:
        Lock()
        {
            ::InitializeCriticalSection(&m_cs);
        }

        ~Lock()
        {
            ::DeleteCriticalSection(&m_cs);
        }

        void acquire()
        {
            ::EnterCriticalSection(&m_cs);
        }

        void release()
        {
            ::LeaveCriticalSection(&m_cs);
        }

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

        CRITICAL_SECTION m_cs;
    };

Now, if you run the above test sample, we will always get unique address of objects on multiple calls.

Note:In an interview, this c++ thread safe singleton class interview question can also be asked as “in singleton class, what code you will synchronize for thread safety?”