2010-09-24 5 views
2

Quelle est la meilleure façon de détruire un objet singleton?détruire un objet singleton

cas A: Environnement fileté simple
cas B: multithread Environnement

Les extraits de l'échantillon (le cas échéant) sera vraiment utile.

[EDIT] Je n'ai pas de cas d'utilisation spécifique J'essaie juste de comprendre que SI TOUT le singleton doit être utilisé, comment le détruire correctement. Si je comprends, à partir des commentaires, il ya 2 scénarios possibles:
1. Détruisez le singleton quand aucun code n'y accède (utilisez des pointeurs intelligents qui se chargeront de détruire l'objet par lui-même en utilisant RAII)
2. Détruire un singleton lors de la sortie du programme indépendamment du fait que du code soit retenu ou non par le singleton. (détruisez explicitement en supprimant l'instance juste avant les sorties principales)

+11

La méthode _best_ est généralement de ne pas avoir de singletons du tout; alors vous n'avez pas à détruire les singletons :-) –

+5

La suppression des fichiers source et d'en-tête devrait faire l'affaire. Puis venez avec un design qui n'utilise pas de singletons. –

+1

Pour plus d'informations sur les raisons pour lesquelles "Singletons are Evil", lisez http://www.lostechies.com/blogs/scottdensmore/archive/2009/08/13/singletons-are-evil-part-2.aspx et son prédécesseur, qui est lié à partir de là. –

Répondre

8

Ne le créez pas en premier lieu! Sérieusement, je vous recommande fortement de reconsidérer votre choix de singleton, en particulier dans un environnement multithread. Au lieu de cela, créez simplement une instance dans main() et transmettez-la dans la hiérarchie des appels où elle est nécessaire.

Vous pouvez utiliser quelque chose comme shared_ptr pour vous assurer que l'objet reste en place jusqu'à ce que plus personne n'en ait besoin.

+0

Je comprends les pièges de l'utilisation de singleton et comme la plupart d'entre vous l'ont mentionné, il devrait être évité, mais si l'on doit utiliser singleton, alors quelles sont les options? Envelopper dans un pointeur intelligent qui utilise le comptage de référence peut être option aussi, je suis à la recherche de solutions possibles ou des solutions de contournement. –

+0

Si vous stockez le pointeur d'instance dans un pointeur intelligent avec une durée de stockage statique, il sera détruit à la fermeture du programme. –

+3

@Als: pourquoi devez-vous utiliser un singleton? Un singleton spécifiquement * ne vous permet pas de le détruire (parce que vous n'avez plus une seule instance) – jalf

1

En multi-thread,

void Singleton::Destroy() 
{ 
    if (instance) { 
     pthread_mutex_lock(&lock); 
     if (instance) { 
      delete instance; 
      instance = 0x0; 
     } 
     pthread_mutex_unlock(&lock); 
    } 
} 

En monothread:

void Singleton::Destroy() 
{ 
    if (instance) { 
     delete instance; 
     instance = 0x0; 
    } 
} 
+2

ne voulez-vous pas dire 'if (instance)'? – filipe

+0

@Arpan - Merci! Mais le vrai problème, je pense, est de s'assurer que ces méthodes de destruction sont appelées à la toute fin, lorsqu'aucune ressource n'a plus besoin du singleton. J'essaie de trouver de bonnes solutions pour ce problème. –

+0

Ceci peut mener à plusieurs instances de votre singleton sur de vrais systèmes multiprocesseurs avec un cache par CPU ou même selon la façon dont le compilateur utilise les registres. Vous devez vous assurer que "instance" provient de la mémoire principale avant de vérifier par exemple! = 0. C'est l'une des fonctions du mutex sur les machines modernes. Ce lien (http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html) concerne principalement Java, mais il fonctionne également pour C++. – Arkadiy

1

La réaction contre le dernier de l'utilisation excessive de la décennie de singletons semblent être en bonne santé, bouffi, mais ils ne sont pas tout à fait mal ou injustifié ... la programmation concerne les compromis et la pratique, et il est difficile de généraliser (généralement ;-P). Par tous les moyens revoir la conception et voir si vous pouvez utilement se débarrasser d'eux, mais si non - ainsi soit-il.

De toute façon, si vous voulez comprendre les compromis, vous ne pouvez pas faire mieux que de commencer en lisant Modern C++ Design d'Alexandrescu, qui consacre un chapitre aux alternatives pour Singletons. Fondamentalement, vous posez une question idiote ici parce que nous ne savons pas quelles contraintes opérationnelles vos singleton (s) ont ... quelles interactions potentielles, quelles ressources ils peuvent avoir besoin d'utiliser et s'ils peuvent être rouverts après avoir été fermé etc .. Alors, crachez-le ou se contenter de réponses stupides ;-P.

0

Laissant de côté la question de savoir si c'est une bonne idée.
Ce que nous devrions faire dans une question séparée!

class S 
{ 
    private: 
     S() {}    // private constructor 
     S(S const&);   // Undefined copy constructor 
     S& operator(S const&) // Undefined assignment operator 

    public: 
     static S& getInstance() 
     { 
      /* 
      * It is guaranteed to be built on first use 
      * and correctly destroyed at the end of the application 
      */ 
      // Need guard for multi-threaded systems (but not on gcc) 
      MULT_THREAD_GUARD; 
      static S theOnlyInstance; 
      return theOnlyInstance; 
     } 
}; 

L'initialisation multi-thread de l'objet est le seul véritable problème. Vous pouvez gérer cela de deux façons.

  • Vous pouvez mettre un garde pour vous assurer qu'un seul thread peut entrer dans la méthode getInstance() lorsque vous faites multi-thread builds (IFF vous utilisez gcc ce n'est pas nécessaire car il plante le code requis automatiquement garantie l'objet est seulement initialisé une fois).
  • L'autre technique consiste simplement à s'assurer que l'instance est initialisée avant la création de tous les threads. Pour ce faire, appelez getInstance() dans main.Rappelez-vous que si vous faites cela, vous pouvez aussi bien avoir une variable globale que vous détruisez le principal avantage des singletons sur les variables globales (initialisation paresseuse). Non que les variables globales soient bien meilleures que les singletons.

Exemple Garde

// Then in your platform agnostic header file 
#ifndef MULTI_THREAD 
#define MULT_THREAD_GUARD  /* Nothing needed in single threaded code */ 
#else 
#if defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 3) && (__GNUC_MINOR__ > 1))) 
#define MULT_THREAD_GUARD  /* Nothing required for GCC 3.2 onwards */ 
#elif defined(YOUR_COMPILERS_MACRO) 
#define MULT_THREAD_GUARD  do { /* Put compiler lock code here */ } while (false) 
#else 
#error "ADD MULTI Thread GUARD for you compiler here" 
#endif 
1

Si vous allez utiliser une approche globale, je préfère quelque chose comme ceci:

class GlobalThing{ /* ... */ }; 

GlobalThing *global_thing = 0; 

// ... 

int main(){ 
    GlobalThing gt(/* ... */); 
    global_thing = > 

    // create threads 
    // ... 
    // kill threads 
} 

Cela vous donne:

  1. Un facilement durée de vie identifiée pour l'objet global.
  2. Nettoyage de ressources de la manière typique RAII. Les points ci-dessus signifient qu'il fonctionne dans un environnement multithread sans se soucier des verrous, etc., car aucun thread n'existera avant ou après la durée de vie de gt. Eh bien, dans certains environnements, vous pouvez sortir de main() et d'autres threads continueront à fonctionner, mais c'est une manière terrible d'architecturer votre programme pour diverses raisons.

Qu'est-ce que vous avez encore à vous soucier:

  1. pour Initialisation de globals. Mais, contrairement au Static Initialization (and Destruction) Order Fiasco, cette technique vous donne l'avantage de définir l'ordre d'initialisation et de destruction des globaux (si vous les définissez tous de cette manière).
  2. Quelque chose d'autre, j'en suis sûr.
+0

L'une des choses "quelque chose d'autre" serait que si quelque chose référence le pointeur global avant les exécutions principales, par exemple: autre initialisation d'objet global/statique. Cela ne veut pas dire que ce soit architecturalement bon non plus, mais c'est un autre cas qui arrive en vrai code ... – Nick

+0

Cela ressemble à un moyen raisonnable de changer le Singleton en un «bon comportement» (je ne crois pas que je dis this) variable globale. –

2

Vous pourriez utiliser atexit() si vous ne tenez compte que du nettoyage après des coupures réussies.

Questions connexes