C ++ programmers, come and write the simplest Singleton mode.

Source: Internet
Author: User

C ++ programmers, come and write the simplest Singleton mode.

Every programmer must be very familiar with the singleton mode in the design mode. In the past, we had to write the following code to use C ++ to implement a singleton mode:

1 class CSingleton 2 {3 private: 4 CSingleton () // The constructor is private 5 {6} 7 static CSingleton * m_pInstance; 8 public: 9 static CSingleton * GetInstance () 10 {11 if (m_pInstance = NULL) // determine whether to call 12 m_pInstance = new CSingleton (); 13 return m_pInstance; 14} 15} for the first time };

Of course, this code is correct in a single-threaded environment, but race condition occurs when the code is obtained in a multi-threaded environment, therefore, in order to implement the singleton mode in a multi-threaded environment, we first think of using the synchronous mechanism to correctly protect our shared data, therefore, the code in the single-threaded environment instance mode is changed to the following:

1 class CSingleton 2 {3 private: 4 CSingleton () // The constructor is private 5 {6} 7 static CSingleton * m_pInstance; 8 mutex CTX; 9 public: 10 static CSingleton * GetInstance () 11 {12 MIB. lock (); 13 if (m_pInstance = NULL) // determine whether to call 14 m_pInstance = new CSingleton (); 15. unlock (); 16 return m_pInstance; 17} 18 };

Correct. The problem is that each call to the GetInstance function enters the critical section, especially in the case of heavy contention, our Great programmers found that we don't have to get the lock every time we call the GetInstance function. We only need to synchronize the lock when we first call the new instance. So the Great programmers invented the famous DCL Technique, that is, Double Check Lock. The Code is as follows:

 1 Widget* Widget::pInstance{ nullptr }; 2 Widget* Widget::Instance() { 3     if (pInstance == nullptr) { // 1: first check 4         lock_guard<mutex> lock{ mutW }; 5         if (pInstance == nullptr) { // 2: second check 6             pInstance = new Widget();  7         } 8     }  9     return pInstance;10 }

Some time ago, this code was considered correct, but a group of Great programmers found the bug! In addition, joint registration indicates that the code is incorrect. To explain why an error occurs, you need to be familiar with the memory model. Here I will not explain it in detail. One sentence is the third line of code in this Code: if (pInstance = nullptr) and row 6 code pInstance = new Widget (); the synchronization is not correct, in some cases, the new return address is assigned to the pInstance variable, and the Widget is not fully constructed yet, when another thread then runs to the third line, it will not enter if and return an incomplete instance object for the user to use, resulting in a serious error. When C ++ 11 does not come out, we can only insert two memory barriers to solve this error. However, C ++ 11 has been around for several years, among them, I think the most important thing is the introduction of the memory model. From then on, C ++ 11 can also identify the thread concept!

Therefore, after C ++ 11, we can implement the DCL mode correctly across platforms. The Code is as follows:

 1 atomic<Widget*> Widget::pInstance{ nullptr }; 2 Widget* Widget::Instance() { 3     if (pInstance == nullptr) {  4         lock_guard<mutex> lock{ mutW };  5         if (pInstance == nullptr) {  6             pInstance = new Widget();  7         } 8     }  9     return pInstance;10 }

The default memory_order_seq_cst of the atomic class in C ++ 11 ensures correct synchronization of 3 and 6 lines of code. As the above atomic class requires some performance loss, therefore, we can write an optimized version:

 1 atomic<Widget*> Widget::pInstance{ nullptr }; 2 Widget* Widget::Instance() { 3     Widget* p = pInstance; 4     if (p == nullptr) {  5         lock_guard<mutex> lock{ mutW };  6         if ((p = pInstance) == nullptr) {  7             pInstance = p = new Widget();  8         } 9     } 10     return p;11 }

However, considering the wide application of the singleton model, the C ++ Committee 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() {4     std::call_once(create, [=]{ instance = make_unique<widget>(); });5     return instance;6 }

It can be seen that the above Code is quite concise compared with the previous sample code, !!! Yes, !!!! In C ++ memory model, The initialization of such a variable is defined to occur the first time control passes through its declaration; for multiple threads calling the function, this means there's the potential for a race condition to define first. therefore, we will 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 Suter's words, the code implementation is "Best of All.

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.