Implementation of singleton mode (C + +)

Implement singleton mode

The purpose of singleton pattern is to make an object of a class the only instance in the system. The class created by the method of singleton mode has only one instance in the current process (if necessary, it may belong to singleton in a thread, for example, only the same instance is used in the thread context)

1. Hungry man model

/*
 * Hungry man model
 * 1. Thread safe because the class is instantiated when the code runs
 * 2. However, deferred loading is not supported, that is, it cannot be created when in use
 */
class Singleton {
public:=
    static Singleton *getInstance();
private:
    Singleton() {
        cout << "new Singleton" << endl;
    }
    Singleton(const Singleton &) = delete;
    Singleton& operator=(const Singleton &) = delete;
    static Singleton *instance;
};

Singleton *Singleton::instance = new Singleton();
Singleton *Singleton::getInstance() {
    return instance;
}

2. Lazy mode

The implementation of lazy mode can be used to create class objects, but it is often not thread safe, so it needs to be locked, which also leads to the problems caused by a variety of locking methods

  1. Simple locking

    /*
     * Lazy mode
     * 1. Not thread safe
     * 2. It supports delayed loading and can be re created when in use
     */
    class Singleton {
    public:
        static Singleton *getInstance();
    private:
        Singleton() {
            cout << "new Singleton" << endl;
        }
        Singleton(const Singleton &) = delete;
        Singleton& operator=(const Singleton &) = delete;
        static Singleton *instance;
        static std::mutex m_mutex;
    };
    
    Singleton *Singleton::instance = nullptr;
    std::mutex Singleton::m_mutex;
    
    // Simple locking
    Singleton *Singleton::getInstance() {
        std::lock_guard<std::mutex> lock(m_mutex); // Lock
        if (instance == nullptr) {
            instance = new Singleton();
        }
        return instance;
    }
    

    As shown in line 24 of the code, a large lock is added when acquiring a singleton object, but this locking method has extremely low efficiency and low concurrency, which is equivalent to serial execution

  2. Simple locking – locking inside if

    // Simple locking if internal
    Singleton *Singleton::getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(m_mutex);
            instance = new Singleton();
        }
        return instance;
    }
    

    Locking in the if can improve the concurrency problem caused by the first method, but it also introduces a new problem. This is not thread safe, because if both threads go to line 3 of the above code at the same time, one thread gets the lock and the other stops at line 3 to wait for the lock to be released, When the thread that gets the lock creates a new Singleton object and returns to release the lock, another thread can continue to get the lock and create a new Singleton object. Thus, there are two Singleton objects, which is obviously inconsistent with the design of Singleton mode.

  3. Double detection

    // Double detection
    Singleton *Singleton::getInstance() {
        if (instance == nullptr) {
            std::lock_guard<std::mutex> lock(m_mutex);
            if (instance == nullptr) {
                instance = new Singleton();
            }
        }
        return instance;
    }
    

    Double detection avoids the problem of the second locking method mentioned above. After locking, if judgment is performed again to avoid the problem of new objects again. However, double detection will also lead to a new problem - instruction rearrangement.

  4. Static internal variable

    After C++11, the initialization operation of static variable is optimized into thread safe operation

    So we can use static internal variables to implement thread safe singleton mode

    // Static internal variable C++11
    class Singleton {
    public:
        static Singleton &getInstance();
    private:
        Singleton() {
            cout << "new Singleton" << endl;
        }
        Singleton(const Singleton &) = default;
        Singleton& operator=(const Singleton &) = delete;
    };
    
    
    Singleton &Singleton::getInstance() {
        static Singleton instance;
        return instance;
    }
    

Keywords: C++ Design Pattern Singleton pattern

Added by dkphp2 on Fri, 17 Dec 2021 20:19:02 +0200