¿Es Singleton
segura la siguiente implementación, usando la inicialización diferida, del hilo (Singleton de Meyers)?
static Singleton& instance()
{
static Singleton s;
return s;
}
Si no, ¿por qué y cómo hacer que sea seguro para subprocesos?
Respuestas:
En C ++ 11 , es seguro para subprocesos. De acuerdo con la norma ,
§6.7 [stmt.dcl] p4
:El soporte de GCC y VS para la función ( Inicialización dinámica y destrucción con concurrencia , también conocido como Magic Statics en MSDN ) es el 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 de doble verificación" que analiza 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 Básicamente es la forma más sencilla de garantizar la concurrencia adecuada en todas las plataformas, mientras que la mayoría de las formas de variantes de patrón de bloqueo doblemente verificadas pueden sufrir condiciones de carrera en ciertas arquitecturas , a menos que las instrucciones se intercalen con barreras de memoria estratégicamente colocadas.
fuente
Para responder a su pregunta sobre por qué no es seguro para subprocesos, no es porque la primera llamada a
instance()
debe llamar al constructorSingleton s
. Para ser seguro para subprocesos, 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 subprocesos). Los compiladores a menudo implementan esto usando una simple verificación e incremento de un booleano estático, pero no en una sección crítica. Algo así como el siguiente pseudocódigo:Así que aquí hay un Singleton seguro para subprocesos simple (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
CRITICAL_SECTION
antes de quemain()
se llame. Idealmente, se usaría una verdadera clase de sección crítica RAII que pueda manejar las excepciones que podrían ocurrir cuando se realiza la sección crítica, pero eso está más allá del alcance de esta respuesta.La operación fundamental es que cuando
Singleton
se solicita una instancia de , se toma un bloqueo, se crea Singleton si es necesario, luego se libera el bloqueo y se devuelve la referencia Singleton.Hombre, eso es un montón de basura para "hacer un mundo mejor".
Los principales inconvenientes de esta implementación (si no dejé pasar algunos errores) es:
new Singleton()
lanza, el bloqueo no se liberará. Esto se puede solucionar mediante el uso de un verdadero objeto de bloqueo RAII en lugar del simple que tengo aquí. Esto también puede ayudar a hacer que las cosas sean portátiles si usa algo como Boost para proporcionar un contenedor independiente de la plataforma para la cerradura.main()
se llame; si la llama antes (como en la inicialización de un objeto estático), las cosas podrían no funcionar porqueCRITICAL_SECTION
podrían no inicializarse.fuente
new Singleton()
tira?new Singleton()
lanza definitivamente hay un problema con la cerradura. Se debe usar una clase de bloqueo RAII adecuada, algo así comolock_guard
de Boost. Quería que el ejemplo fuera más o menos autónomo, y ya era un monstruo, así que dejé la seguridad de excepción (pero lo llamé). Tal vez debería arreglar eso para que este código no se corte y pegue en algún lugar inapropiado.Mirando el 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 del estándar se implemente ampliamente, Singleton de Meyer será la implementación preferida.
Ya no estoy de acuerdo con muchas respuestas. 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.
fuente
La respuesta correcta depende de tu compilador. Puede decidir que sea seguro para subprocesos; no es "naturalmente" seguro para los hilos.
fuente
En la mayoría de las plataformas, esto no es seguro para subprocesos. (Agregue el descargo de responsabilidad habitual que explica que el estándar C ++ no conoce los subprocesos, por lo que, legalmente, no dice si lo es o no).
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 ++ y los peligros del bloqueo de doble verificación" de Scott Meyers y Andrei Alexandrescu es un tratado bastante bueno sobre el tema de los singletons seguros para subprocesos.
fuente
Como dijo MSalters: Depende de la implementación de C ++ que uses. Consulta la documentación. En cuanto a la otra pregunta: "Si no, ¿por qué?" - El estándar C ++ aún no menciona nada sobre hilos. Pero la próxima versión de C ++ conoce los subprocesos y declara explícitamente que la inicialización de locales estáticos es segura para los subprocesos. Si dos hilos llaman a tal función, un hilo realizará una inicialización mientras que el otro bloqueará y esperará a que termine.
fuente