Instancia Singleton declarada como variable estática del método GetInstance, ¿es seguro para subprocesos?

He visto implementaciones de patrones de Singleton donde la variable de instancia se declaró como variable estática en el método GetInstance. Me gusta esto:

SomeBaseClass &SomeClass::GetInstance() { static SomeClass instance; return instance; } 

Veo los siguientes aspectos positivos de este enfoque:

  • El código es más simple, porque es el comstackdor el responsable de crear este objeto solo cuando se llama GetInstance por primera vez.
  • El código es más seguro, porque no hay otra manera de obtener referencia a la instancia, pero con el método GetInstance y no hay otra manera de cambiar la instancia, pero dentro del método GetInstance.

¿Cuáles son los lados negativos de este enfoque (excepto que esto no es muy OOP-ish)? ¿Es esto seguro para subprocesos?

En C ++ 11 es seguro para subprocesos:

§6.7 [stmt.dcl] p4 Si el control ingresa la statement al mismo tiempo que la variable se está inicializando, la ejecución simultánea deberá esperar a que se complete la inicialización.

En C ++ 03:

  • En g ++ es seguro para subprocesos.
    Pero esto se debe a que g ++ explícitamente agrega código para garantizarlo.

Uno de los problemas es que si tienes dos singletons, intentan y se usan durante la construcción y la destrucción.

Lea esto: Encontrar problemas de orden de inicialización estática de C ++

Una variación de este problema es si se accede al singleton desde el destructor de una variable global. En esta situación, el singleton definitivamente ha sido destruido, pero el método get devolverá una referencia al objeto destruido.

Hay formas de evitar esto, pero están desordenadas y no vale la pena hacerlo. Simplemente no acceda a un singleton desde el destructor de una variable global.

Una definición más segura pero fea:
Estoy seguro de que puede agregar algunas macros apropiadas para arreglar esto

 SomeBaseClass &SomeClass::GetInstance() { #ifdef _WIN32 Start Critical Section Here #elif defined(__GNUC__) && (__GNUC__ > 3) // You are OK #else #error Add Critical Section for your platform #endif static SomeClass instance; #ifdef _WIN32 END Critical Section Here #endif return instance; } 

No es seguro para subprocesos como se muestra. El lenguaje C ++ no dice nada en los hilos, por lo que no tiene garantías inherentes del lenguaje. Tendrá que usar primitivas de sincronización de plataforma, por ejemplo, Win32 :: EnterCriticalSection (), para proteger el acceso.

Su enfoque particular sería problemático b / c el comstackdor insertará algún código (no seguro para subprocesos) para inicializar la instance estática en la primera invocación, lo más probable será que el cuerpo de la función comience la ejecución (y por lo tanto antes de que pueda invocarse cualquier sincronización) .)

Usar un puntero de miembro global / estático para SomeClass y luego inicializar dentro de un bloque sincronizado sería menos problemático de implementar.

 #include  namespace { //Could be implemented as private member of SomeClass instead.. boost::shared_ptr g_instance; } SomeBaseClass &SomeClass::GetInstance() { //Synchronize me eg ::EnterCriticalSection() if(g_instance == NULL) g_instance = boost::shared_ptr(new SomeClass()); //Unsynchronize me eg :::LeaveCriticalSection(); return *g_instance; } 

No he comstackdo esto, así que es solo para fines ilustrativos. También se basa en la biblioteca de impulso para obtener la misma duración (o la misma) como su ejemplo original. También puede usar std :: tr1 (C ++ 0x).

Según las especificaciones, esto también debería funcionar en VC ++. Alguien sabe si lo hace?

Simplemente agregue la palabra clave volátil. El comstackdor visual de c ++ debe generar mutexes si el documento en msdn es correcto.

 SomeBaseClass &SomeClass::GetInstance() { static volatile SomeClass instance; return instance; } 

Comparte todas las fallas comunes de las implementaciones de Singleton, a saber:

  • Es inestable
  • No es seguro para subprocesos (esto es bastante trivial para ver si imagina dos subprocesos entrando en la función al mismo tiempo)
  • Es una pérdida de memoria

Recomiendo nunca usar Singleton en ningún código de producción.