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