2009-06-27 9 views
13

Recuerdo haber leído que las variables estáticas declaradas dentro de los métodos no son seguras para subprocesos. (Ver What about the Meyer's singleton? como se ha mencionado por Todd Gardner)Variables estáticas a prueba de roscas sin muteos?

Dog* MyClass::BadMethod() 
{ 
    static Dog dog("Lassie"); 
    return &dog; 
} 

Mi biblioteca genera código C++ para los usuarios finales para compilar como parte de su aplicación. El código que genera necesita inicializar variables estáticas de una manera multiplataforma segura para subprocesos. Me gustaría usar boost::call_once para excluir la inicialización de la variable pero luego los usuarios finales están expuestos a la dependencia de Boost.

¿Hay alguna manera de hacerlo sin forzar dependencias adicionales en los usuarios finales?

+0

También puede vincular estáticamente en bibliotecas de impulso a las suyas, luego puede usar impulsar todo lo que quiera sin temor a molestar a sus usuarios. – Ben

+0

posible duplicado de [¿Es segura la inicialización de la variable estática local en C++ 11?] (Http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe-in-c11) –

+1

@LucianAdrianGrijincu, esta pregunta es anterior a C++ 11 (se publicó en 2009) por lo que, aunque su pregunta está relacionada, no es un duplicado estricto. Gracias por el enlace. – Gili

Respuesta

10

Tiene razón en que la inicialización estática como esa no es hilo de seguridad (here es un artículo sobre lo que el compilador lo convertirá en)

Por el momento, no hay un estándar, hilo de seguridad, manera portátil para inicializar singletons estáticos. Se puede usar el bloqueo comprobado doble, pero necesita bibliotecas de threading potencialmente no portátiles (consulte una discusión here).

Aquí hay algunas opciones si la seguridad es una necesidad hilo:

  1. no ser perezoso (cargado): Inicializar durante la inicialización estática. Podría ser un problema si otra estática llama a esta función en su constructor, ya que el orden de inicialización estática no está definido (consulte here).
  2. Uso impulso (como usted ha dicho) o Loki
  3. rollo su propia Singleton en sus plataformas soportadas (probablemente debe evitarse a menos usted es un experto en el roscado)
  4. bloquear un mutex cada vez que se necesita acceso. Esto podría ser muy lento.

Ejemplo de 1:

// in a cpp: 
namespace { 
    Dog dog("Lassie"); 
} 

Dog* MyClass::BadMethod() 
{ 
    return &dog; 
} 

Ejemplo para 4:

Dog* MyClass::BadMethod() 
{ 
    static scoped_ptr<Dog> pdog; 
    { 
    Lock l(Mutex); 
    if(!pdog.get()) 
     pdog.reset(new Dog("Lassie")); 
    } 
    return pdog.get(); 
} 
+0

Terminé mordiendo la viñeta y usando 'boost :: call_once()' – Gili

+7

esta respuesta ahora está obsoleta: http://stackoverflow.com/questions/8102125/is-local-static-variable-initialization-thread-safe- in-c11 – Yankes

2

La única manera que conozco para garantizar que no tendrá problemas de threads con recursos no protegidos al igual que su "static Dog" es para que sea un requisito de que todos están ejemplificados antes se crean todos los hilos.

Esto podría ser tan simple como simplemente documentar que tienen que llamar a una función MyInit() en el hilo principal antes de hacer cualquier otra cosa. A continuación, construye MyInit() para crear instancias y destruir un objeto de cada tipo que contenga una de esas estadísticas.

La única otra alternativa es poner otra restricción sobre cómo pueden usar el código generado (usar Boost, Win32 hilos, etc.). Cualquiera de esas soluciones es aceptable, en mi opinión, está bien generar reglas que deben seguir.

Si no siguen las reglas establecidas por su documentación, entonces todas las apuestas están apagadas. La regla de que deben llamar a una función de inicialización o depender de Boost no es irracional para mí.

3

Una forma que podía hacerlo, que no requiere un mutex para la seguridad de los subprocesos es hacer que el singleton un archivo estático, en lugar de la función estática:

static Dog dog("Lassie"); 
Dog* MyClass::BadMethod() 
{ 
    return &dog; 
} 

La instancia Dog será inicializado antes de que el hilo principal carreras. Las variables estáticas de archivo tienen un problema famoso con el orden de inicialización, pero mientras Dog no dependa de ninguna otra estática definida en otra unidad de traducción, esto no debería ser motivo de preocupación.

+0

Recuerdo que puede haber problemas si tiene inicializadores globales en DLL en Windows, pero no puedo recordar detalles, y en realidad solo es relevante si el código está en una DLL de todas formas: D – olliej

+0

Mi código * does * reside en una DLL :) ¿Qué problemas debería tener en cuenta? – Gili

+0

zurcido si lo sé. Mi mejor estimación es que el constructor se ejecutará una vez por cada vez que se cargue el dll, por lo que si el dll se descarga y se vuelve a cargar, se volverá a crear para romper el contrato de Singleton, pero no se puede hacer mucho al respecto –

2

AFAIK, la única vez que esto se ha hecho de forma segura y sin mutexes o inicialización antes de instancias global es en Mateo Wilson's Imperfect C++, que explica cómo hacer esto usando un "mutex de giro". No estoy cerca de mi copia, así que no puedo decirlo con más precisión en este momento.

IIRC, hay algunos ejemplos del uso de esto dentro de las bibliotecas STLSoft, aunque no recuerdo qué componentes en este momento.

4

No estoy seguro de si esto es lo que quiere decir o no, pero puede eliminar la dependencia de refuerzo en los sistemas POSIX llamando al pthread_once en su lugar. Supongo que tendrías que hacer algo diferente en Windows, pero evitar eso es exactamente por lo que impulsar tiene una biblioteca de hilos en primer lugar, y por qué la gente paga el precio de depender de ello.

Hacer cualquier cosa "thread-safe" está intrínsecamente relacionado con la implementación de sus hilos. Debe confiar en algo, aunque solo sea el modelo de memoria dependiente de la plataforma. Simplemente no es posible en C++ 03 puro asumir nada en absoluto sobre los hilos, que están fuera del alcance del lenguaje.