C + + programmers, let's write the simplest single-case pattern.

Source: Internet
Author: User

Presumably every programmer is very familiar with the singleton pattern in the design pattern, and in the past we implemented a singleton pattern with C + + to write the following code:

1 classCsingleton2 {3 Private:4Csingleton ()//The constructor is private5     {6     }7     StaticCsingleton *m_pinstance;8  Public:9     StaticCsingleton *getinstance ()Ten     { One         if(m_pinstance = = NULL)//determines whether the first call is AM_pinstance =NewCsingleton (); -         returnm_pinstance; -     } the};

Of course, this code in a single-threaded environment is correct, but when the multi-threaded environment when the code will appear race condition, so in order to be able to implement a single case mode in the multi-threaded environment, we first think of the use of synchronization mechanism to properly protect our shared data, So in the multi-threaded environment, the singleton pattern code becomes the following:

1 classCsingleton2 {3 Private:4Csingleton ()//The constructor is private5     {6     }7     StaticCsingleton *m_pinstance;8 Mutex MTX;9  Public:Ten     StaticCsingleton *getinstance () One     { AMtx.Lock(); -         if(m_pinstance = = NULL)//determines whether the first call is -M_pinstance =NewCsingleton (); the Mtx.unlock (); -         returnm_pinstance; -     } -};

Correct is correct, the problem is every call getinstance function to enter the critical section, especially in the case of heavy contention function will become a system performance bottleneck, our great programmer found that we do not have to call the getinstance function every time to get the lock, It is only necessary to synchronize at the first instance of new, so the great program engineers invented the famous DCL technique, double Check Lock, which is the following code:

1widget*Widget::p instance{nullptr};2widget*widget::instance () {3     if(pinstance = = nullptr) {//1:first Check4Lock_guard<mutex>Lock{MUTW};5         if(pinstance = = nullptr) {//2:second Check6Pinstance =Newwidgets ();7         }8     } 9     returnPinstance;Ten}

There was a time when the code was thought to be correct, but a great number of the handlers found the bug! And the joint letter says the code is wrong. To explain why the error occurred, the reader needs to be very familiar with the memory model, here I do not elaborate, a word is in this code, the third line of code: if (pinstance = = nullptr) and the sixth line of code pinstance = new Widget (); Without proper synchronization, in some cases new returns the address assignment to the pinstance variable while the widget is not yet fully constructed, and when another thread then runs to the third row, it will not enter if and return an incomplete instance object to the user, caused a serious error. In the c++11 did not come out, only by inserting two memory barrier to solve this error, but C++11 has been around for several years, which I think the most important is the introduction of the memory model, from this c++11 can also recognize the concept of thread!

So, after we have the c++11, we can implement the DCL mode correctly across the platform, the code is as follows:

1Atomic<widget*>Widget::p instance{nullptr};2widget*widget::instance () {3     if(Pinstance = =nullptr) { 4Lock_guard<mutex>Lock{MUTW};5         if(Pinstance = =nullptr) { 6Pinstance =Newwidgets ();7         }8     } 9     returnpinstance;Ten}

The default memory_order_seq_cst of the atomic class in c++11 guarantees correct synchronization of 3, 6 lines of code, because the above atomic requires some performance loss, so we can write an optimized version:

1Atomic<widget*>Widget::p instance{nullptr};2widget*widget::instance () {3widget* p =pinstance;4     if(p = =nullptr) { 5Lock_guard<mutex>Lock{MUTW};6         if(p = pinstance) = =nullptr) { 7Pinstance = P =Newwidgets ();8         }9     } Ten     returnp; One}

However, the C + + committee, considering the wide application of the singleton pattern, provides a more convenient component to accomplish the same function:

 1  static  unique_ptr<widget>  widget::instance;  2  static   Std::once_flag Widget::create;  3  widget& Widget::get_instance () { Span style= "color: #008080;" >4  std::call_once (Create, [=]{instance = Make_unique<widget> ();});  5  return   instance;  6 } 

It can be seen that the above code compared to the previous example code is quite concise, but!!! Yes, but!!!! In the C++memory model to the static local variable, said: the initialization of such a variable is defined to occur the first time contro L passes through its declaration; For multiple threads calling the function, this means there's the potential for a race condition to define first. So we're going to get A c++11 implementation of the simplest and most efficient singleton mode:

1 widget& widget::get_instance () {2     static  widget instance; 3     return instance; 4 }

In Herb Sutter's words, this code implementation is "Best of all".

C + + programmers, let's write the simplest single-case pattern.

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.