C + + cross-platform single-instance mode singleton

Source: Internet
Author: User

Singleton mode is a kind of common software design pattern. In its core structure, it contains only one special class, which is called a singleton class, and the singleton mode guarantees that there is only one instance of a class in the system and that the instance is easily accessible to the outside world.

Scenario: For some classes in the system, only one instance is important, for example, there can be more than one printing process in a system, but there can only be one working printing process;

Based on Wikipedia's description of the singleton pattern:

ensure that a class has only one instance and provides global access to the instance .

So, we can get a very important characteristic of a singleton pattern:

A class has only one object.


One, single-threaded model:

For an ordinary class, we can generate n objects at will by the constructor, obviously, in order to make a class can have only one object, then we should first set the constructor to private function , disable its copy and assignment ability. The following code is then available:

class singleton{    private:        Singleton () {};};

Thus we cannot generate any singleton object in the main function;

Singleton s; // Error

So how do we solve this problem?


Here we are based on the nature of the static keyword , bypassing the constructor for private restrictions, so the code is as follows;

class singleton{    public:        static Singleton *getinstance ()//  Static quiescent store        returnnew Singleton;}  // New Type    Private :        Singleton () {};};

This is called in the main function:

int Main (intconstChar *argv[]) {    << singleton::getinstance ( ) << Endl;     << singleton::getinstance () << Endl;     return 0 ;}

We find that the first address of the printed two objects is different, that is, the return value of the two singleton::getinstance () function is not the same , because this function is a new object for us each time.
The problem with the above code is that there is no guarantee that the resulting object is unique .

So how do we solve this problem?

In a function member we set a static pointer whose initial value is null, and naturally this pointer is owned by this class and does not belong to a specific object , and each time the pointer is empty, it indicates whether an object has been produced. The code is as follows:

//There are pitfalls under multithreadingclasssingleton{ Public:        StaticSingleton *getinstance ()//static quiescent storage area        {             if(Psingleton = =NULL) Psingleton=NewSingleton;//New Type            returnPsingleton; }      Private: Singleton () {}; Staticsingleton*Psingleton;}; Singleton*singleton::p Singleton = NULL;//definition of a static member function

Then we test this code with the main function above, and the result shows that the value produces an object. This shows that we have implemented a simple singleton pattern.

Second, multi-threaded single-case mode:

Is there really no problem with the previous piece of code?

Display is not, we consider a process with multiple threads in the case, the code is as follows:

#include <iostream>#include"Thread.h"#include<stdlib.h>using namespacestd;//Multithreading Vulnerabilityclasssingleton{ Public:        StaticSingleton *getinstance ()//static quiescent storage area        {             if(Psingleton = =NULL) {:: Sleep (1);//to prevent the kernel from getting too fastPsingleton =NewSingleton;//New Type            }            returnPsingleton; }      Private: Singleton () {}Staticsingleton*Psingleton;}; Singleton*singleton::p Singleton =NULL;classMyThread: Publicthread{ Public:        voidrun () {cout<< singleton::getinstance () <<Endl; cout<< singleton::getinstance () <<Endl; }};//This example tests the risk of multithreadingintMainintargcConst Char*argv[]) {    //Singleton ();    Const intKsize = A;    MyThread Threads[ksize]; inti;  for(i =0; I! = ksize; i++) {Threads[i].start (); }         for(i =0; I! = ksize; i++) {threads[i].join (); } cout<<"Hello"<<Endl; return 0;}

The test results are as follows:

0xb13004680xb13004980x9f887280xb13004980xb13004780xb13004980xb11004880xb13004980xb13004880xb13004980xb13004980xb13004980x9f887380xb13004980x9f887480xb13004980xb11004780xb13004980xb11004980xb13004980xb11004680xb13004980xb11004a80xb11004a8

We have found that many of the addresses are different. So how did the above problem arise?

Because there is more than one thread in the above process, it is assumed that the code is a/b when multiple threads are started, and when thread A calls the run function, it stays a second after executing an if (psingleton==null) statement.
At this point B will also execute to the IF (psingleton==null) statement, and then B will determine whether the Psingleton is empty, when a is not a new object, so B will enter into the IF statement of the function body,
So A/b will be new for each object. Therefore, the above results are produced.

So how do we solve this problem?

Naturally we would think of introducing a mutex , so that the following code is formed:

//The rest of the code hasn't changed .classsingleton{ Public:        StaticSingleton *getinstance () {mutex_.Lock(); if(Pinstance_ = =NULL) {:: Sleep (1); Pinstance_=NewSingleton;            } mutex_.unlock (); returnPinstance_; }    Private: Singleton () {}StaticSingleton *Pinstance_; Staticmutexlock mutex_;}; Singleton*singleton::p instance_ = NULL;//definition of static memberMutexlock singleton::mutex_;

Obviously, passing the test proves that this method is feasible. But there is a very serious problem here: the problem of efficiency .
mutexes can greatly reduce the concurrency of the system, because each call is locked to unlock operations .

So we wrote the following test:

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;}//Test timeint64_t Getutime () {structTimeval TV; :: Memset (&AMP;TV,0,sizeofTV); if(Gettimeofday (&AMP;TV, NULL) = =-1) {perror ("Gettimeofday");    Exit (Exit_failure); } int64_t Current=tv.tv_usec; Current+ = Tv.tv_sec * +* +; returnCurrent ;}

In the above code, we open 100 threads, each thread calls 1M times singleton::getinstance ()
The test results are as follows:

6729 ms

What method can solve this problem?

Dual lock mode is used

To improve our 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_;};

What is the benefit of seeing that we have a double-check pattern in getinstance?

Security: The internal mutex is already in use and the code is safe anyway.
Efficiency problem: When an instance is generated, pinstance is not empty, and each subsequent thread accesses the outermost if judgment to return directly , and does not perform the subsequent locking unlock operation

We test again and the results are as follows:

Cost:  458  ms

We've seen just three lines of code, but it's more than 10 times times more efficient!

Thread.h&thread.cpp for reference: Linux component package three: Thread

Mutexlock.h&mutexlock.cpp for reference: One of the Linux component packages: Mutexlock

C + + cross-platform single-instance mode singleton

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.