variable usar una todas sirve restricción que para métodos metodos metodo mantiene llamar las instancias hagan estáticos estatico estatica ejemplos declarados contenido como clase atributos almacenan c++ multithreading c++11 static singleton

c++ - una - restricción de java al usar atributos y métodos estáticos.



¿Por qué este singleton estático de C++ nunca se detiene? (5)

He implementado un singleton (versión estática) en C ++. Sé toda la controversia sobre este patrón y los posibles problemas de seguridad de subprocesos, pero tengo curiosidad por la razón por la cual esta implementación exacta no se detendrá. El programa nunca se cierra, permanece en un estado de interbloqueo al final.

singleton.h:

#pragma once #include <thread> #include <atomic> class Singleton { public: static Singleton& getInstance(); private: std::thread mThread; std::atomic_bool mRun; Singleton(); ~Singleton(); void threadFoo(); };

singleton.cpp

#include "singleton.h" Singleton& Singleton::getInstance() { static Singleton instance; return instance; } Singleton::Singleton() { mRun.store(true); mThread = std::thread(&Singleton::threadFoo, this); } Singleton::~Singleton() { mRun.store(false); if(mThread.joinable()) mThread.join(); } void Singleton::threadFoo() { while(mRun) { } }

main.cpp

#include "singleton.h" int main() { Singleton::getInstance(); return 0; }

Lo que ya sé:

  • el hilo termina
  • El hilo principal está atascado en la unión.
  • tiene algo que ver con la estática, si hago público al constructor y creo una instancia de Singleton en main () terminará correctamente.

Utilizando Visual Studio 2012. Gracias por tu consejo.


En el subproceso principal, después de que termine main() , el CRT adquiere el bloqueo de salida y llama a su instancia estática destructor, que espera a que salga el subproceso en segundo plano.

En el subproceso de fondo, una vez que finaliza su función de subproceso, el CRT intenta adquirir el bloqueo de salida para realizar un trabajo de terminación de subproceso. Esto se bloquea para siempre porque el bloqueo de salida se mantiene en el subproceso principal, que está esperando que este subproceso salga.

Es un punto muerto simple causado por la implementación de CRT. La conclusión es que no puede esperar la finalización de subprocesos en un destructor de instancia estática en Windows.



Lo he rastreado para void __cdecl _lock(int locknum) dentro de mlock.c . Cuando main() termina, el hilo principal va allí y entra en la sección crítica EnterCriticalSection( _locktable[locknum].lock ); . Luego se llama a Singleton destructor y el otro hilo intenta ingresar a la misma sección crítica, pero no puede, y comienza a esperar que el hilo principal abandone la sección crítica. El hilo principal, a su vez, espera el otro hilo. Así que supongo que es un error.


Ok, gracias a todos por sus consejos. Aparentemente, esta implementación de patrones da como resultado un punto muerto en VC ++.

Después de investigar un poco más, encontré esta implementación basada en la mecánica de C ++ 11 que está funcionando en VC ++.

singleton.h

#pragma once #include <thread> #include <atomic> #include <memory> #include <mutex> class Singleton { public: static Singleton& getInstance(); virtual ~Singleton(); private: static std::unique_ptr<Singleton> mInstance; static std::once_flag mOnceFlag; std::thread mThread; std::atomic_bool mRun; Singleton(); void threadFoo(); };

singleton.cpp

#include "singleton.h" std::unique_ptr<Singleton> Singleton::mInstance = nullptr; std::once_flag Singleton::mOnceFlag; Singleton& Singleton::getInstance() { std::call_once(mOnceFlag, [] { mInstance.reset(new Singleton); }); return *mInstance.get(); } Singleton::Singleton() { mRun.store(true); mThread = std::thread(&Singleton::threadFoo, this); } Singleton::~Singleton() { mRun.store(false); if(mThread.joinable()) mThread.join(); } void Singleton::threadFoo() { while(mRun.load()) { } }

ACTUALIZAR

Parece que Microsoft es consciente de este problema. En los foros de VC ++, un usuario llamado "dlafleur" informó sobre esta publicación: https://connect.microsoft.com/VisualStudio/feedback/details/747145


Ver [basic.start.term] en el Estándar:

Si se utiliza un objeto de biblioteca estándar o una función no permitida dentro de los manejadores de señales (18.10) que no ocurre antes (1.10) de la destrucción de objetos con duración de almacenamiento estático y la ejecución de las funciones registradas de std :: atexit (18.5), El programa tiene un comportamiento indefinido. [Nota: si se utiliza un objeto con una duración de almacenamiento estática que no ocurre antes de la destrucción del objeto, el programa tiene un comportamiento indefinido. Terminar cada subproceso antes de una llamada a std :: exit o la salida de main es suficiente, pero no es necesario, para satisfacer estos requisitos. Estos requisitos permiten a los administradores de subprocesos como objetos de duración de almacenamiento estático. "Nota final"