This article is original, reproduced please specify: http://www.cnblogs.com/inevermore/p/4014577.html
According to Wikipedia, the description of the singleton pattern is:
Ensure that a class has only one instance and provides global access to the instance.
From this passage, we can draw the most important features of the Singleton pattern:
A class has only one object at most
Single Thread Environment
For an ordinary class, we can generate the object arbitrarily, so we need to set the constructor of the class to private in order to avoid generating too many classes.
So we write the first step:
class singleton{public: private: Singleton () {}};
At this point, the object cannot be generated directly in main:
// ERROR
So we want to get the instance, only with the help of the function inside the class, so we add an inner function, and it must be a static function (think about why):
class singleton{public: static Singleton *getinstance () { return New Singleton; } Private : Singleton () {}};
OK, we can use this function to generate the object, but every time we go to new, we cannot guarantee uniqueness, so we save the object in a static pointer, and then each time we get the object, we first check whether the pointer is empty:
classsingleton{ Public: StaticSingleton *getinstance () {if(Pinstance_ = = NULL)//Switching of Threads{:: Sleep (1); Pinstance_=NewSingleton; } returnPinstance_; }Private: Singleton () {}StaticSingleton *Pinstance_;}; Singleton*singleton::p instance_ = NULL;
We test in main:
cout << singleton::getinstance () <<<< singleton::getinstance () << Endl;
You can see that the same object was generated, and the singleton pattern was initially successfully written.
Considerations in multi-threaded environments
But is the code really going to be okay?
I have written the following tests:
classTestthread: Publicthread{ Public: voidrun () {cout<< singleton::getinstance () <<Endl; cout<< singleton::getinstance () <<Endl; }};intMainintargcChar Const*argv[]) { //test proves that there is a competition problem in this code under multithreadingTestthread threads[ A]; for(intIX =0; IX! = A; ++ix) {Threads[ix].start (); } for(intIX =0; IX! = A; ++ix) {threads[ix].join (); } return 0;}
Note here that in order to achieve the effect, I made the following changes:
if // Switching of Threads { :: Sleep (1); New Singleton;}
This intentionally causes the thread to switch .
The printing results are as follows:
0xb13004680xb13004980x9f887280xb13004980xb13004780xb13004980xb11004880xb13004980xb13004880xb13004980xb13004980xb13004980x9f887380xb13004980x9f887480xb13004980xb11004780xb13004980xb11004980xb13004980xb11004680xb13004980xb11004a80xb11004a8
It is clear that our code cannot withstand the scrutiny of multithreading.
What to do? Locking So we improved again:
classsingleton{ Public: StaticSingleton *getinstance () {mutex_.Lock(); if(Pinstance_ = = NULL)//Switching of ThreadsPinstance_ =NewSingleton; Mutex_.unlock (); returnPinstance_; }Private: Singleton () {}StaticSingleton *Pinstance_; Staticmutexlock mutex_;}; Singleton*singleton::p Instance_ =NULL; Mutexlock singleton::mutex_;
At this point the test, no problem.
However, the mutex can greatly reduce the concurrency of the system, because each call to lock, equal to a group of people over the bridge.
I have written a test as follows:
classTestthread: Publicthread{ Public: voidrun () {Const intKcount = +* +; for(intIX =0; IX! = Kcount; ++ix) {singleton::getinstance (); } }};intMainintargcChar Const*argv[]) { //Singleton S; ERRORint64_t StartTime=Getutime (); Const intKsize = -; Testthread Threads[ksize]; for(intIX =0; IX! = ksize; ++ix) {Threads[ix].start (); } for(intIX =0; IX! = ksize; ++ix) {threads[ix].join (); } int64_t endTime=Getutime (); int64_t Difftime= EndTime-StartTime; cout<<"Cost :"<< Difftime/ +<<"Ms"<<Endl; return 0;}
100 threads are opened, each calling 1M times getinstance, where getutime is defined as follows:
int64_t Getutime () { struct timeval TV; :: memset (0sizeof TV); if (Gettimeofday (&TV, NULL) = =-1) { perror ("gettimeofday"); Exit (exit_failure); } = tv.tv_usec; + + ; return Current ;}
The result of the operation is:
6914 ms
Dual lock mode is used
The above test, we still can't see the performance problem, I improved the code again:
classsingleton{ Public: StaticSingleton *getinstance () {if(Pinstance_ = =NULL) {mutex_.Lock(); if(Pinstance_ = = NULL)//Switching of ThreadsPinstance_ =NewSingleton; Mutex_.unlock (); } returnPinstance_; }Private: Singleton () {}StaticSingleton *Pinstance_; Staticmutexlock mutex_;}; Singleton*singleton::p Instance_ =NULL; Mutexlock singleton::mutex_;
As you can see, I used the double check mode in GetInstance, where is the advantage of this code?
The internal use of mutexes, the code is reliable anyway
New after the first instance, each subsequent thread accesses the outermost if judgment to return directly , without the overhead of locking
I run the test again (the test code does not change) and the results are as follows:
438 ms
Aha, more than 10 times times the performance gap, it can be seen that our improvements are effective, only three lines of code, but brought more than 10 times times the efficiency of the increase!
End
This method of writing became DCLP (double-check-locking-pattern) mode, which was once thought to be absolutely correct, but later it was pointed out that in some cases this approach would be executed in a random order, so refer to Scott's C + + and the Perils of double-checked Locking-scott Meyer
C++singleton's DCLP (double lock) implementation and performance evaluation