The introduction

Singleton pattern is a common design pattern in engineering. As the name implies, singleton refers to an object with only one instantiation within the program running cycle. In the interview process, the implementation of thread-safe singleton pattern is also a high frequency examination point, this article is based on the interview, listing C++ thread-safe singleton pattern implementation methods.

Singleton implementation

Depending on the time of instantiation, there are two main categories: hungry and lazy.

  1. The hungry. The instance is created before it is first fetched.
    class Singleton {
     public:
      static Singleton* GetInstance(a);
     private:
      static Singleton* instance_;
    };
    Singleton* instance_ = new Singleton(a);// It has been created
    
    Singleton* Singleton::GetInstance(a) {
      return instance_;
    }
    Copy the code
  2. Idlers. It is created when the instance is first obtained.
    class Singleton {
     public:
      static Singleton* GetInstance(a);
     private:
      static Singleton* instance_;
    };
    Singleton* instance_ = nullptr;  // Static variable initialization, nullptr by default
    
    Singleton* Singleton::GetInstance(a) {
      if (instance_ == nullptr) {
        instance_ = new Singleton(a);// instance_ is created when instance_ is empty
      }
      return instance_;
    }
    Copy the code

Thread safety

Thread safety is simply a matter of multiple threads reading and writing to the same shared object inconsistently. To be clear, thread-safety against singletons refers to the operation of fetching instances, not the fetched instance object itself. Let’s examine the thread safety of the two implementations:

  • In the hungry man way, callGetInstance()Will return the created instance directly, which can be considered read operations and thus thread-safe;
  • In the lazy way, you can seeGetInstance()The comment for intance_ is “created when intance_ is null”, not “created when the first instance is fetched”, for which one possible problem is that both threads are enteredif (instance_ == nullptr)In the judgment branch of, all executedinstance_ = new Singleton()This step, the result is obvious,SingletonInstantiated twice, thus causing thread insecurity.

Thread-safe lazy singleton pattern

Now that we know why threads are unsafe, let’s go straight to the safety example:

  1. Add a mutex
    class Singleton {
     public:
      static Singleton* GetInstance(a);
     private:
      static Singleton* instance_;
      static mutex mtx_;
    };
    Singleton* Singleton::instance_ = nullptr;  // Static variable initialization, nullptr by default
    mutex Singleton::mtx_;
    
    Singleton* Singleton::GetInstance(a) {
      unique_lock<mutex> lock(mtx_);
      if (instance_ == nullptr) {
        instance_ = new Singleton(a);// instance_ is created when instance_ is empty
      }
      return instance_;
    }
    Copy the code
  2. Double check lock (DCL)
    class Singleton {
     public:
      static Singleton* GetInstance(a);
     private:
      static Singleton* instance_;
      static mutex mtx_;
    };
    Singleton* Singleton::instance_ = nullptr;  // Static variable initialization, nullptr by default
    mutex Singleton::mtx_;
    
    Singleton* Singleton::GetInstance(a) {
      if (instance_ == nullptr) {
        unique_lock<mutex> lock(mtx_);
        if (instance_ == nullptr) {
          instance_ = new Singleton(a);// instance_ is created when instance_ is empty}}return instance_;
    }
    Copy the code
  3. Internal static variable
    class Singleton {
     public:
      static Singleton* GetInstance(a);
    };
    
    Singleton* Singleton::GetInstance(a) {
      static Singleton instance;  / /! Specify C++11 compilation options
      return &instance;
    }
    Copy the code
  4. unique_lock+call_once
    class Singleton {
     public:
      static Singleton& GetInstance(a);
     private:
      static unique_ptr<Singleton> instance_;
    };
    
    Singleton& Singleton::GetInstance(a) {
      static once_flag flag;
      call_once(flag, [&]() {
        instance_.reset(new Singleton());
      });
      return *instance_;
    }
    Copy the code

Other problems

  • DCL methods may be optimized by some compilers, resulting in out-of-order problems. Reference Design Patterns – Singleton patterns – Stories About C++ + – BookStack
  • How can a created singleton be freed automatically? Refer to the design pattern – singleton pattern – “Stories About C Plus Plus” – BookStack to implement an embedded garbage collection class

reference

  1. C++11 uses smart Pointers to improve singleton mode _HelloKandy’s blog -CSDN blog
  2. Design Pattern – Singleton Pattern – Stories About C++ Plus – BookStack
  3. C++ thread safety singleton pattern summary – xiaolin coding – blogs.com