Simple analysis of single-instance mode and thread safety (Linux environment C + + version)

Source: Internet
Author: User
Tags mutex

What is a singleton mode

The singleton mode is a common pattern in design mode, defined as ensure a class have only one instance, and provide a global point of access to it. (Make sure that there is only an instance of a class, and instantiate it yourself and Provide this instance to the entire system)

In the case of Zen in design mode, it is, in a system, requiring a class to have and only one object, if there are multiple there will be "adverse reactions", such as the following scenarios may be used in the singleton mode

    • Environments that require unique serial numbers to be generated
    • A shared access point or shared data, such as a counter on a Web page, can be used throughout the project to keep the value of the counter in single-instance mode (to cover thread safety later on) without having to record every refresh record in the database.
    • Creating an object requires consuming too many resources, such as accessing IO and database resources
    • A large number of static constants and static methods (such as tool classes) need to be defined


Advantages of Singleton mode
    • Because Singleton mode has only one instance in memory, reducing memory overhead, especially when an object needs to be created, destroyed frequently, and performance cannot be optimized when created or destroyed, the role of the singleton pattern is obvious
    • Reduce the performance cost of the system, when the production of an object requires more resources, such as reading data, generating other dependent objects, you can create a singleton object when the application starts, the memory
    • Avoid multiple uses of resources, such as writing a file action, because only one instance exists in memory, avoiding simultaneous write operations on the same resource
    • The system can set the global access point, optimize the shared resources, for example, you can design a singleton class, responsible for all the data table mapping processing


Single-Instance mode implementation
Single Thread implementation

Say so much, but how is the pattern designed?

To avoid inheriting this class from subsequent derived classes and mistakenly initializing the class multiple times (that is, multiple backups in memory), we should set its constructor to private and leave the unique initialization to the static function, such as C + +.

#include <iostream>template<typename t>class singleton{public:static t& instance () {if (flag_init) { Flag_init = False;flag_destory = True;init ();} return *value_;} static void Init () {value_ = new T ();} static void Destory () {if (flag_destory) {flag_destory = False;suicide ();}} static void Suicide () {delete value_;} Private:static t* Value_;      //Initialize pointer static bool Flag_init only once;    //Indicates whether it is possible to initialize the flag bit static bool flag_destory; Indicates if the flag bit Singleton () {}            //Private function was destroyed, resulting in the inability to initialize this class multiple times ~singleton () {}     & nbsp    //private function, which causes the class to not be destroyed multiple times};//static variables must be initialized outside template<typename t>t* singleton<t>::value_ = NULL; Template<typename T>bool singleton<t>::flag_destory = false;template<typename T>bool Singleton< T>::flag_init = true;//Test If the test case is available for class Example{public:example () {value = 0;} ~example () {}void tool () {value++;std::cout << value<< Std::endl;} Private:int value;}; int MAIn (int argc, char *argv[]) {example& Ex1 = Singleton<example>::instance (); Ex1.tool (); example& ex2 = Singleton<example>::instance (); Ex2.tool (); Singleton<example>::d estory (); Singleton<example>::d estory (); return 0;}

output two times is 1 and 2, respectively, is the same object is manipulated, only one copy in memory, multiple destruction, invalid destruction will be ignored

Multithreading implementation

But this is OK, if the multi-threaded, if it is multi-threaded, at the same time to modify the creation or destruction of the two bool value can occur error.

So how to solve, someone proposed DCLP (double checked locking pattern) mechanism, combined with mutex mutex, ask two times method.

Analysis:

When multiple threads enter instance () at the same time, the first if (Value_ = = null) is found to be true, after which the thread that gets the lock is initialized directly through the second if (value_ = = null) , and then the lock is released. The other thread gets to the second if, and the instance is initialized, returning the instance directly, without initializing it two times.

Implemented as follows

<span style= "FONT-SIZE:12PX;" > #include <iostream> #include <unistd.h> #include "Mutex.h" Template<typename t>class singleton{ Public:static t& Instance () {if (Value_ = = NULL) {Mutexlockguard Guard (mutex_);//This represents a zone lock, implemented with code, see the Linux multithreaded Server programming "Aboutspeaker" if (Value_ = = NULL) {init ();}} return *value_;} static void Init () {value_ = new T ();} Private:static t* value_;static Mutexlock mutex_; Singleton () {}~singleton () {}};//static variable must be initialized outside template<typename t>t* singleton<t>::value_ = null;template <typename T>mutexlock singleton<t>::mutex_;//Tests if a single test class is available for the classes Example:boost::noncopyable{public: Example () {value = 0;} ~example () {}void tool () {value++;std::cout << value<< "";} Private:int value;}; void* thread (void*arg) {example& ex3 = Singleton<example>::instance (); Ex3.tool (); return NULL;} int main (int argc, char *argv[]) {pthread_t tid;pthread_create (&tid, NULL, Thread, NULL);p thread_create (&tid, NULL, thread, NULL);p thread_create (&tid, NULL, thread, NULL);p thread_create (&tid, NULL, thread, NULL);p thread_create (&tid, NULL, thread, NULL);p Thread_ Create (&tid, NULL, thread, NULL);p thread_create (&tid, NULL, thread, NULL);p thread_create (&tid, NULL, Thread, NULL);p thread_create (&tid, NULL, thread, NULL); example& Ex1 = Singleton<example>::instance (); Ex1.tool (); sleep (1); return 0;} </span>
Compile-time remember to add parameters ... -pthread

Mute x.h Code


Run detection, as if multithreading is not a problem, but this really can, foreign gods come to face

Meyers an article of the Great God

Meyers points out that new Singleton, this step (that is, new in the Init () function) will break down into three behaviors when it really runs.

    1. Allocating Memory

    2. Constructing an Instance Object

    3. Point pointer to allocated memory

These three instructions may be re-queued by the CPU, and then the order of execution will change, such as 3->1->2

In general, there will be no exception, because disorderly execution is the CPU of an optimization means (details of its own, a lot of content, do not unfold narration), and the outer layer has a mutex protection. However, our mutex protection is conditional, only after the first if judgment to enter the mutex protection range, and this condition is affected by 3, if the CPU first executed 3, then another CPU at the same time 1 judgment, found that the pointer is not empty, directly return the object for the upper layer to use, And then you return to the object that has not been constructed at all!

pthread_once Solving DCLP problems

Pthread_once () is guaranteed by the Pthreads library that a function is executed only once in a multithreaded way, implemented as follows:

#include <iostream> #include <boost/noncopyable.hpp>template<typename t>class singleton{public: Static t& instance () {pthread_once (&ponce_, &singleton::init); return *value_;} static void Init () {value_ = new T ();} Private:static pthread_once_t ponce_;static t* value_;}; Template<typename t>pthread_once_t singleton<t>::p once_ = Pthread_once_init;template<typename T>T * Singleton<t>::value_ = NULL;

Some people will ask, pthread_once is how to solve this problem, see the source

<span style= "FONT-SIZE:12PX;" >int__pthread_once (Once_control, init_routine)     pthread_once_t *once_control;     void (*init_routine) (void);  {/  * XXX depending on whether the lock_in_once_t are defined use     a global LOCK variable or one which are part of the Pthread_once_t     object.  *  /if (*once_control = = Pthread_once_init)    {         lll_lock (Once_lock, lll_private);      /* XXX This implementation isn't complete.  It doesn ' t take     cancelation and fork to account.  *      /if (*once_control = = Pthread_once_init)    {         init_routine ();       *once_control =! Pthread_once_init;    }         Lll_unlock (Once_lock, lll_private);    }     return 0;} </span>
Look at the following, found that this implementation is actually DCLP mechanism, do not believe you see

if (*once_control = = Pthread_once_init) appeared two times, the principle and before we write the same, then why it is reliable, in fact, the key is

<span style= "FONT-SIZE:12PX;" >  lll_lock (Once_lock, lll_private);</span>

After inquiry and search the Big God blog, this is a based on the GCC embedded instructions macro, different hardware platform implementation is not the same, we only remember the role is good, the role is

    • Do not rearrange this instruction with the preceding command, that is, the preceding instruction must be executed sequentially.
    • Do not cache variables to registers

So to avoid the CPU command disorderly rearrangement, some people on C + + mature, will say that I give before the variable plus The volatile keyword is good, right, but Linux C + +, here specifically c++98 standard volatile very chicken, Do not do the role of memory barrier can not achieve the role of control order, so it is difficult to achieve, directly with Pthread_once, if the new version can try the original DCLP practice, here are detailed blog, I will not repeat

C++11 Standard under DCLP


Some trivia

The previous code has inherited boost::noncopyable, this is a common practice, the Noncopyable class to set constructors and destructors to protected permissions, this way the class can be called, the outside class cannot be called, The outside caller is not able to construct a new subclass from assignment and copy.

    #include <boost/noncopyable>          class Example:public boost::noncopyable      {public      :          Example () {};         Example (int i) {};      };            int main ()      {          Example CL1 ();          Example Cl2 (1);                Example Cl3 (CL1);    Error          //example Cl4 (Cl2);    Error                return 0;      }  

Finally, the singleton model of Uncle Hao is explained, this is in Java as an example

Single-instance mode Java


This article in reading books and access to online data completion, if there are shortcomings, please put forward

Resources:

"Linux multithreaded Server Programming" Aboutspeaker

"Zen of Design pattern" Qin Xiaobo



Simple analysis of single-instance mode and thread safety (Linux environment C + + version)

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.