unity patrones patron example ejercicios ejemplo diseño code c++ multithreading design-patterns

c++ - patrones - ¿Es segura la implementación de Meyers del hilo del patrón Singleton?



singleton c# example code (6)

¿El siguiente hilo de implementación [...] es seguro?

En la mayoría de las plataformas, esto no es seguro para subprocesos. (Añádase el descargo de responsabilidad habitual que explica que el estándar de C ++ no conoce los hilos, por lo que, legalmente, no dice si lo es o no).

Si no, ¿por qué [...]?

La razón por la que no lo es es que nada impide que más de un hilo ejecute simultáneamente s ''constructor.

cómo hacer que sea seguro?

"C ++ y los peligros del bloqueo doblemente controlado" por Scott Meyers y Andrei Alexandrescu es un tratado bastante bueno sobre el tema de los singleton seguros para subprocesos.

¿Es segura la implementación siguiente, utilizando la inicialización lenta del subproceso Singleton ( Singleton Meyers)?

static Singleton& instance() { static Singleton s; return s; }

Si no, ¿por qué y cómo hacerlo seguro?


Como dijo MSalters: depende de la implementación de C ++ que utilice. Verifique la documentación. En cuanto a la otra pregunta: "Si no, ¿por qué?" - El estándar de C ++ aún no menciona nada sobre los hilos. Pero la próxima versión de C ++ es consciente de los hilos y establece explícitamente que la inicialización de los locales estáticos es segura para subprocesos. Si dos hilos llaman a esa función, un hilo realizará una inicialización mientras que el otro bloqueará y esperará a que termine.


En C++11 , es seguro para subprocesos. De acuerdo con el standard , §6.7 [stmt.dcl] p4 :

Si el control ingresa la declaración simultáneamente mientras se está inicializando la variable, la ejecución concurrente deberá esperar hasta que se complete la inicialización.

El soporte de GCC y VS para la característica ( Inicialización dinámica y destrucción con concurrencia , también conocida como Magic Statics en MSDN ) es la siguiente:

Gracias a @Mankarse y @olen_gam por sus comentarios.

En C++03 , este código no era seguro para subprocesos. Hay un artículo de Meyers llamado "C ++ y los peligros del bloqueo controlado doblemente" que trata las implementaciones seguras de subprocesos del patrón, y la conclusión es, más o menos, que (en C ++ 03) bloqueo completo alrededor del método de creación de instancias es básicamente la forma más simple de garantizar la concurrencia adecuada en todas las plataformas, mientras que la mayoría de las formas de variantes de patrones de bloqueo comprobadas pueden sufrir condiciones de carrera en ciertas arquitecturas , a menos que las instrucciones se intercalen estratégicamente coloca barreras de memoria.


En cuanto al siguiente estándar (sección 6.7.4), explica cómo la inicialización local estática es segura para subprocesos. Entonces, una vez que esa sección de estándar se implemente ampliamente, Meyers Singleton será la implementación preferida.

No estoy de acuerdo con muchas respuestas ya. La mayoría de los compiladores ya implementan la inicialización estática de esta manera. La única excepción notable es Microsoft Visual Studio.


La respuesta correcta depende de tu compilador. Puede decidir hacerlo threadsafe; no es "naturallly" threadsafe.


Para responder a su pregunta acerca de por qué no es seguro para el hilo, 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 siguiente pseudocódigo:

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 simple seguro para subprocesos (para Windows). Utiliza un contenedor de clase simple para el objeto Windows CRITICAL_SECTION para que el compilador pueda inicializar automáticamente CRITICAL_SECTION antes de llamar a 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.

La operación fundamental es que cuando se solicita una instancia de Singleton , se toma un bloqueo, se crea Singleton si es necesario, luego 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 mucha basura para "hacer un mundo mejor".

Los principales inconvenientes de esta implementación (si no dejé pasar algunos errores) es:

  • si se lanza un new Singleton() , el bloqueo no se liberará. 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 la seguridad del subproceso cuando se solicita la instancia de Singleton después de llamar a main() ; si lo llama antes (como en la inicialización de un objeto estático) es posible que las cosas no funcionen porque es posible que CRITICAL_SECTION no se inicialice.
  • se debe tomar 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é fallan las cosas como la técnica de bloqueo de doble verificación), consulte los documentos relacionados en la respuesta de Groo .