I have a doubt related to Singleton and multithread programming in C++
Following you can see an example code of a Singleton class with a variable named shared.
I create 1000 threads that modify (+1) that variable of my Singleton global instance. The final value of shared is 1000 but I would expect this value to be under 1000 since I am not protecting this variable for concurrency.
Is the code really thread safe because the class is Singleton or it just happened to be lucky and the value is 1000 but it can perfectly be less than 1000?
#include <iostream>
using namespace std;
class Singleton {
private:
Singleton() {shared = 0;};
static Singleton * _instance;
int shared;
public:
static Singleton* Instance();
void increaseShared () { shared++; };
int getSharedValue () { return shared; };
};
// Global static pointer used to ensure a single instance of the class.
Singleton* Singleton::_instance = NULL;
Singleton * Singleton::Instance() {
if (!_instance) {
_instance = new Singleton;
}
return _instance;
}
void * myThreadCode (void * param) {
Singleton * theInstance;
theInstance = Singleton::Instance();
theInstance->increaseShared();
return NULL;
}
int main(int argc, const char * argv[]) {
pthread_t threads[1000];
Singleton * theInstance = Singleton::Instance();
for (int i=0; i<1000; i++) {
pthread_create(&threads[i], NULL, &myThreadCode, NULL);
}
cout << "The shared value is: " << theInstance->getSharedValue() << endl;
return 0;
}
You got lucky…
In reality, the most likely issue with what you’re observing has to-do with the fact that the time it takes to increment the value of your singleton on your specific machine is less than the time it takes the operating system to allocate the resources to launch an individual pthread. Thus you never ended up with a scenario where two threads contend for the unprotected resources of the singleton.
A much better test would have been to launch all of your pthreads first, have them block on a barrier or condition variable, and then perform the increment on the singleton once the barrier’s condition of all the threads being “active” is met … at that point you would have been much more likely to have seen the sorts of data-races that occur with non-atomic operations like an increment operation.