La implementación segura de C++ singleton, el método de implementación segura de doble verificación (bloqueo de doble verificación)

descripción general

  • El modo singleton se refiere a garantizar que solo se pueda generar una instancia de una clase en todo el ciclo de vida del sistema para garantizar la unicidad de la clase.

  • Características de la clase Singleton

    • Los constructores y destructores son tipos privados, cuyo propósito es prohibir la construcción externa y los destructores.
    • Los constructores de construcción de copia y asignación son tipos privados, el propósito es prohibir la copia y asignación externas, para garantizar la singularidad de la instancia.
    • Hay una función estática para obtener una instancia en la clase, a la que se puede acceder globalmente
  • El patrón singleton se puede dividir en estilo perezoso y estilo hambriento, la diferencia entre ambos radica en el tiempo de creación de la instancia:

    • Estilo perezoso: significa que la instancia no existe cuando el sistema se está ejecutando, y la instancia se creará y usará solo cuando se necesite. (La seguridad del subproceso debe considerarse de esta manera)
    • Estilo chino hambriento: significa que una vez que el sistema se está ejecutando, la instancia se inicializa y crea, y cuando sea necesario, se puede llamar directamente. (en sí mismo seguro para subprocesos, no hay problema de subprocesos múltiples)

Implementación china hambrienta de C ++ 11

Singleton& Singleton::getInstance() {
    
    
    static Singleton instance;
    return instance;
}

En general, se recomienda usar este método simple, que es seguro y fácil de usar.

Implementación perezosa de C++11

No es fácil lograr un estilo perezoso seguro

  • El método tradicional de implementación de doble verificación es el siguiente:
Singleton* Singleton::getInstance() {
    
    
	if (m_instance)
		return m_instance;
    Lock lock;      // scope-based lock, released automatically when the function returns
    if (m_instance == NULL) {
    
    
        m_instance = new Singleton;
    }
    return m_instance;
}

Estrictamente hablando, la implementación anterior no es completamente segura, porque la primera lectura de m_instance no está protegida por un candado o similar;

  • Basado en el atómico proporcionado por C++ 11, se puede implementar un singleton de estilo perezoso más seguro.El código es el siguiente:
std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

Singleton* Singleton::getInstance() {
    
    
    Singleton* tmp = m_instance.load();
    if (tmp == nullptr) {
    
    
        std::lock_guard<std::mutex> lock(m_mutex);
        tmp = m_instance.load();
        if (tmp == nullptr) {
    
    
            tmp = new Singleton;
            m_instance.store(tmp);
        }
    }
    return tmp;
}
  • El código de prueba completo es el siguiente:

#include <iostream>
#include <string>
#include <mutex>
#include <atomic>
#include <thread>

class Singleton {
    
    

public:

    static Singleton* getInstance() {
    
    
        Singleton* tmp = m_instance.load();
        if (tmp == nullptr) {
    
    
            std::lock_guard<std::mutex> lock(m_mutex);
            tmp = m_instance.load();
            if (tmp == nullptr) {
    
    
                tmp = new Singleton;
                m_instance.store(tmp);
            }
        }
        return tmp;
    }

    ~Singleton() {
    
    
        std::cout << "~Singleton() called!" << std::endl;
    }

    void print() {
    
    

        if (m_instance == nullptr) 
            std::cout << "m_instance is null" << std::endl;
        else
            std::cout << "m_instance is not null" << std::endl;
    }

private:
    Singleton() = default;
    Singleton(const Singleton& Singleton) = default;
    static std::atomic<Singleton*> m_instance;
    static std::mutex m_mutex;
};

std::atomic<Singleton*> Singleton::m_instance;
std::mutex Singleton::m_mutex;

int main()
{
    
    
    Singleton::getInstance()->print();
    std::thread([]{
    
     Singleton::getInstance()->print(); }).detach();
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
	return 0;
}

producción:

m_instance is not null
m_instance is not null

referencia

is-implementation-of-double-checked-singleton-thread-safe double modo de bloqueo de doble verificación en C++11
-checked-locking-is-fixed-in-cpp11

Supongo que te gusta

Origin blog.csdn.net/stallion5632/article/details/126218126
Recomendado
Clasificación