Instance Singleton déclarée comme variable statique de la méthode GetInstance, est-il thread-safe?

j'ai vu des implémentations de modèles de Singleton où la variable d'instance a été déclarée comme variable statique dans la méthode de GetInstance. Comme ceci:

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

je vois les côtés positifs suivants de cette approche:

  • le code est plus simple, parce que c'est le compilateur qui est responsable de la création de cet objet seulement quand GetInstance a appelé pour la première fois.
  • le code est plus sûr, parce qu'il n'y a pas d'autre moyen d'obtenir référence à l'instance, mais avec la méthode GetInstance et il n'y a pas d'autre moyen de changer l'instance, mais à l'intérieur de la méthode GetInstance.

Quels sont les aspects négatifs de cette approche (sauf que ce n'est pas très OOP-ish) ? Est-ce "thread-safe"?

28
demandé sur Trevor Boyd Smith 2009-01-16 06:44:31

4 réponses

En C++11, il est thread-safe:

§6.7 [stmt.dcl] p4 si control entre la déclaration simultanément alors que la variable est initialisée, l'exécution simultanée doit attendre la fin de l'initialisation.

En C++03:

  • sous g++, c'est sans fil.

    Mais c'est parce que g++ ajoute explicitement du code pour le garantir.

un problème est que si vous avez deux Singleton et ils essaient et utilisent l'un l'autre pendant la construction et la destruction.

lisez ceci: Trouver C++ statique de l'ordre d'initialisation des problèmes

une variation sur ce problème est si le singleton est accédé à partir du destructeur d'une variable globale. Dans cette situation, le singleton a définitivement été détruit, mais la méthode get retournera quand même une référence de l'objet détruit.

il y a des moyens de contourner cela, mais ils sont désordonnés et ne valent pas la peine de le faire. Ne ACCÉDEZ PAS à un singleton depuis le destructeur d'une variable globale.

plus Sûr définition, mais laid:

Je suis sûr que vous pouvez ajouter quelques macros appropriées pour ranger cette up

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;
}
35
répondu Martin York 2017-05-23 12:17:47

il n'est pas sûr de fil comme indiqué. Le langage C++ est silencieux sur les threads de sorte que vous n'avez aucune garantie inhérente de la langue. Vous devrez utiliser des primitives de synchronisation de plate-forme, par exemple Win32:: EnterCriticalSection (), pour protéger l'accès.

votre approche particulière serait problématique b / c le compilateur insérera un certain code (non-thread safe) pour initialiser la statique instance sur la première invocation, très probablement il sera avant que le corps de fonction commence l'exécution (et donc avant toute synchronisation peut être invoquée.)

L'utilisation d'un pointeur global/statique de membre à SomeClass puis l'initialisation à l'intérieur d'un bloc synchronisé serait moins problématique à mettre en œuvre.

#include <boost/shared_ptr.hpp>

namespace
{
  //Could be implemented as private member of SomeClass instead..
  boost::shared_ptr<SomeClass> g_instance;
}

SomeBaseClass &SomeClass::GetInstance()
{
   //Synchronize me e.g. ::EnterCriticalSection()
   if(g_instance == NULL)
     g_instance = boost::shared_ptr<SomeClass>(new SomeClass());
   //Unsynchronize me e.g. :::LeaveCriticalSection();
   return *g_instance;
}

Je n'ai pas compilé ceci donc c'est à titre indicatif seulement. Il s'appuie également sur la bibliothèque boost pour obtenir la même durée de vie (ou à peu près) que votre exemple original. Vous pouvez également utiliser std::tr1 (C++0x).

5
répondu Henk 2009-01-16 06:32:39

selon les spécifications, cela devrait aussi fonctionner en VC++. Quelqu'un sait si il fait?

il suffit d'ajouter le mot-clé volatile. Le compilateur visual c++ devrait alors générer des Mutex si le doc sur msdn est correct.

SomeBaseClass &SomeClass::GetInstance()
{
   static volatile SomeClass instance;
   return instance;
}
1
répondu Sal 2012-01-05 08:51:14

il partage tous les défauts communs de Singleton implementations, à savoir:

  • Il est impossible à vérifier
  • il n'est pas sûr de fil (c'est assez trivial pour voir si vous imaginez deux fils entrant dans la fonction en même temps)
  • c'est une fuite de mémoire

je recommande de ne jamais utiliser Singleton dans un code de production.

-6
répondu 1800 INFORMATION 2009-01-16 04:07:43