Para responder a su pregunta acerca de por qué no es seguro para la rosca, no es porque la primera llamada a instance()
debe llamar al constructor para Singleton s
. Para que sea seguro, esto debería ocurrir en una sección crítica, pero no hay ningún requisito en el estándar de que se tome una sección crítica (el estándar hasta la fecha es completamente silencioso en los hilos). Los compiladores a menudo implementan esto usando una comprobación simple y un incremento de un booleano estático, pero no en una sección crítica. Algo así como el pseudocódigo siguiente:
static Singleton& instance()
{
static bool initialized = false;
static char s[sizeof(Singleton)];
if (!initialized) {
initialized = true;
new(&s) Singleton(); // call placement new on s to construct it
}
return (*(reinterpret_cast<Singleton*>(&s)));
}
Así que aquí hay un Singleton sencillo seguro para subprocesos (para Windows). Utiliza un contenedor de clase simple para el objeto CRITICAL_SECTION de Windows para que podamos hacer que el compilador inicialice automáticamente el CRITICAL_SECTION
antes de llamar al main()
. Idealmente, se utilizaría una verdadera clase de sección crítica de RAII que puede tratar con las excepciones que pueden ocurrir cuando se lleva a cabo la sección crítica, pero eso está más allá del alcance de esta respuesta.
El funcionamiento fundamental es que cuando se solicita una instancia de Singleton
, se realiza un bloqueo, se crea Singleton si es necesario, se libera el bloqueo y se devuelve la referencia de Singleton.
#include <windows.h>
class CritSection : public CRITICAL_SECTION
{
public:
CritSection() {
InitializeCriticalSection(this);
}
~CritSection() {
DeleteCriticalSection(this);
}
private:
// disable copy and assignment of CritSection
CritSection(CritSection const&);
CritSection& operator=(CritSection const&);
};
class Singleton
{
public:
static Singleton& instance();
private:
// don't allow public construct/destruct
Singleton();
~Singleton();
// disable copy & assignment
Singleton(Singleton const&);
Singleton& operator=(Singleton const&);
static CritSection instance_lock;
};
CritSection Singleton::instance_lock; // definition for Singleton's lock
// it's initialized before main() is called
Singleton::Singleton()
{
}
Singleton& Singleton::instance()
{
// check to see if we need to create the Singleton
EnterCriticalSection(&instance_lock);
static Singleton s;
LeaveCriticalSection(&instance_lock);
return s;
}
Hombre - eso es un montón de basura para "hacer un mundo mejor".
Los principales inconvenientes de este IMPLEMENTACIÓN (si no dejar que algunos insectos se deslizan a través) es:
- si
new Singleton()
tiros, la cerradura no se dará a conocer. Esto se puede solucionar utilizando un verdadero objeto de bloqueo RAII en lugar del simple objeto que tengo aquí. Esto también puede ayudar a que las cosas sean portátiles si usa algo como Boost para proporcionar un contenedor independiente de la plataforma para el bloqueo.
- esto garantiza seguridad de subprocesos cuando se solicita la instancia de Singleton después de llamar al
main()
; si lo llama antes (como en la inicialización de un objeto estático) es posible que las cosas no funcionen porque CRITICAL_SECTION
podría no inicializarse.
- Se debe realizar un bloqueo cada vez que se solicite una instancia. Como dije, esta es una implementación sencilla y segura. Si necesita uno mejor (o si desea saber por qué elementos como la técnica de bloqueo de doble verificación son defectuosos), consulte el papers linked to in Groo's answer.
¿Puede alguien explicar por qué esto no es seguro para subprocesos? Los artículos mencionados en los enlaces analizan la seguridad de subprocesos utilizando una implementación alternativa (utilizando una variable de puntero, es decir, Singleton * pInstance estática). – Ankur
Ver: http: // stackoverflow.com/questions/449436/singleton-instance-declared-as-static-variable-of-getinstance-method/449823 # 449823 –
Ver: http://stackoverflow.com/questions/1008019/c-singleton-design-pattern/ 1008289 # 1008289 –