Implementation of thread-safe version of singleton mode in C++

The basic definition of singleton mode: there is only one instance at runtime, which is divided into hungry style (instantiated at the beginning) and lazy style (instantiated only when called). The lazy style is implemented below (because I am lazy O(∩_∩)O~ )

Features of singleton pattern:

1. Only one global access point (single interface)

2. Can only be instantiated once (static variable)

3. Cannot assign or copy (write the copy and assignment overloaded functions in private, or set them to delete)

Additional implementation:

Thread safety (locking)

Prevent memory leaks (smart pointers)

The C++ code is as follows

#include <iostream> 
#include <memory> //shared_ptr
#include <mutex>
#include <thread> 
using namespace std;
class Singleton
{
    private:
        Singleton()
        {
            cout << "constructed!" << '\n';
        }

        static mutex mtx;
        static shared_ptr<Singleton> single_instance;//智能指针

    public:

        ~Singleton()
        {
            cout << "destructed!" << '\n';
        }

        Singleton(Singleton&) = delete;
        Singleton* operator=(const Singleton&) = delete;//禁止拷贝与构造

        static shared_ptr <Singleton> getInstance()
        {
            if (single_instance == nullptr)//第一层检验
            {
                cout << "a"<<" ";
                mtx.lock();//上锁
                if (single_instance == nullptr)//第二层检验
                {
                    single_instance = shared_ptr<Singleton>(new Singleton);
                }
                mtx.unlock();//解锁
            }
           return single_instance;
        }
};

shared_ptr<Singleton> Singleton::single_instance = nullptr;//初始化
mutex Singleton::mtx;

void Test()
{
    for (int i = 0; i < 100; i++)
    {
        shared_ptr<Singleton> ptr = Singleton::getInstance();
       // cout << ptr.use_count()<<" ";
    }
}

int main()
{
    thread th1(Test);
    thread th2(Test);

    th1.join();
    th2.join();

    return 0;
}

Running result 1

a constructed!
a destructed!

Running result 2

a a constructed!
destructed!

From the running results, we can see that when two threads try to initialize Singleton at the same time, only one can succeed.

The second result is that both threads initially judged that the Singleton was not instantiated, but one of them added a lock and successfully instantiated it, while the other could not be instantiated.

Q: Why use double judgment?

The first level of judgment is to prevent multiple locks and waste of resources.

The second level of judgment is to prevent repeated creation of instances

Q: Applications and pitfalls of singleton pattern

Application scenario: Only one instantiated object is required, such as a logging system, GUI component (the entire application only requires one GUI component), and the object may be destroyed and created frequently

Defects: All subsystems of the global object can be called, which increases coupling. If a system changes the instance of the singleton and causes a crash, it is difficult to maintain.

Others: The singleton mode can also be implemented using local static variables, which is simpler and will not be described in detail here.

Guess you like

Origin blog.csdn.net/qq_30798083/article/details/130605787