2010-09-21 2 views
5

Dans ce code, pourquoi a-t-il été écrit "Non thread safe"? MerciPourquoi ce code de construction singleton n'est-il pas sécurisé?

class Singleton 
{ 
    private static Singleton _instance; 

    // Constructor is 'protected' 
    protected Singleton() 
    { 
    } 

    public static Singleton Instance() 
    { 
    // Uses lazy initialization. 
    // **Note: this is not thread safe.** 
    if (_instance == null) 
    { 
     _instance = new Singleton(); 
    } 

    return _instance; 
    } 
} 
+0

Langue? L'explication sera similaire, mais pas la même, entre Java et .NET. –

Répondre

24

Si deux threads exécuter le if (_instance == null) contrôle en même temps alors qu'il n'y a pas d'instance singleton créé, ils seront tous les deux essayer d'invoquer new pour créer l'instance singleton et stocker les références à eux dans la même variable .

Étant donné que la référence qu'ils vont essayer de stocker sera partagée entre les threads, cette action ne sera pas adaptée aux threads. En outre, il peut arriver que la création de deux instances de la classe singletone brise le programme. Parce que le singleton ne fournit pas d'exclusion mutuelle sur la propriété _instance.

+11

+1 mais cela peut être utile de mentionner qu'ils n'ont même pas besoin de courir en même temps. Un changement de contexte sur un seul processeur principal ** après ** Thread1 a vérifié la condition mais ** avant ** il assigne 'instance' aurait Thread2 créer le singleton. Quand Thread1 est de nouveau en jeu, il continuera là où il est parti et créera et assignera aussi le singleton. –

+1

Le changement de contexte n'a même pas besoin de se produire à ce moment-là. Avec le modèle de mémoire Java, le thread 2. peut toujours voir le membre _instance comme nul même si le thread 1 a fait l'affectation. – nos

8

Utilisez un verrouillage pour atteindre le fil de sécurité:

Object thisLock = new Object(); 

public static Singleton Instance() 
{ 
    lock (thisLock) 
    { 
     if (_instance == null) 
     { 
      _instance = new Singleton(); 
     } 
    } 

    return _instance; 
} 

Cet exemple est C# - je ne sais pas quel langage de programmation que vous utilisez.

http://msdn.microsoft.com/en-us/library/c5kehkcz(VS.90).aspx

3

Parce qu'il est possible que plusieurs instances du singleton est créé dans ce cas. Supposons que deux threads ont entré la méthode Instance et que l'objet singleton n'est pas encore créé (c'est-à-dire _instance est NULL). Supposons ensuite que le premier thread exécute la condition if et l'entre. Mais avant cela, un commutateur de contexte de thread new se produit et le deuxième thread commence à s'exécuter. Il teste également la condition if et trouve qu'il est NULL et fait un new. Maintenant le premier thread commence à s'exécuter et crée une instance de plus de l'objet.

+0

+1 genre de ce que j'ai écrit dans un commentaire dans ce fil (sans jeu de mots). Toutes les réponses sont donc correctes mais je crois que cette réponse est la plus facile à comprendre. –

5

Sur la base de la réponse de RPM1984:

J'utilise les éléments suivants pour l'objet de verrouillage: object thisLock = typeof(Sinlgeton);

ou tout simplement

... 
lock(typeof(Singleton)) 
{ 
    ... 
} 

pour la performance savy parmi vous:

public Singleton getInstance() 
{ 
    // the first query may save a performance-wise expensive lock - operation 
    if (null == _instance) 
    { 
     lock (typeof(Singleton)) 
     { 
      if (null == _instance) 
      { 
      _ instance = new Singleton() 
      } 
     } 
    } 

    return _instance; 
} 

BTW: Cela s'appelle le dou motif singleton bloqué.

+0

Et [wikipedia] (http://en.wikipedia.org/wiki/Double-checked_locking) lien. :) –

+2

-1: Double strat lock est bon mais vous ne devriez jamais verrouiller sur le type d'une classe. Un objet statique local privé est le meilleur. Vérifiez la première note http://msdn.microsoft.com/en-us/library/system.type.aspx – aqwert

+1

Merci pour cela! Tu vis et tu apprends ... Je n'étais pas au courant de cette note! –

0

Je pense que dans Java, il suffit d'ajouter un indicateur synchronisé à la méthode getInstance. Cela empêchera les autres threads d'entrer dans la méthode tandis qu'un autre est à l'intérieur.

0

Le plus gros problème est que la vérification qu'une instance n'existe pas déjà et son initialisation différée n'est pas une opération atomique.

L'exemple le plus simple est que le thread A vérifie le conditionnel puis cède au thread B; Le thread B vérifie le même conditionnel et crée un nouvel objet et renvoie ce nouvel objet; Le fil B retourne alors au fil A, qui reprend là où il s'était arrêté; Thread A puis va et crée un nouvel objet et le renvoie.

Il y a quelques autres problèmes notables:

  1. La variable _Singleton doit être marquée volatile pour faire en sorte que les écritures sont correctement publiées.
  2. La classe doit être marqué finale ou scellé pour éviter les problèmes avec les sous-classes.
  3. En Java, vous devez remplacer la méthode clone() pour lancer une UnsupportedOperationException.
  4. Si pour une raison quelconque, il y a une exigence de faire la classe sérialisable vous devez également fournir une implémentation pour gérer ce scénario (sinon un client pourrait désérialiser un flux continu pour créer des instances multiples).
1

Cette tournée plus interessting que je pensais au départ:

Donc, la meilleure solution serait

public sealed class Singleton 
{ 
    private static readonly Singletion _instance = new Singleton(); 

    private Singleton() 
    { 
     //do your construction 
    } 

    public static Singleton getInstance() 
    { 
     return _instance; 
    } 
} 

De ma compréhension actuelle l'environnement de programmation (Java, .NET) ne devrait pas d'importance pour cette solution.

Des idées ou des commentaires?

Pour en savoir plus, je l'ai creusé:

Edit: Quant à Java, il devrait également fonctionner:

Maintenant, si quelqu'un fait une version de sauvegarde C++ serait complet ... (je loin de C++ trop longtemps pour se rappeler les détails ...)

0

Si le singleton est pas cher pour créer, et principale exigence est que tout dans l'application voir le même, une autre approche est:

 
    If TheSingleton Is Nothing Then 
    Dim newSingleton As New Singleton 
    If Interlocked.CompareExchange(TheSingleton, newSingleton, Nothing) IsNot Nothing Then 
     newSingleton.Dispose ' If applicable 
    End If 
    End If 

Si deux fils passent le « Si » test avant soit fait le CompareExchange, deux singletons seront créés. Cependant, seul le premier singleton à être transmis à CompareExchange sera utilisé pour n'importe quoi; l'autre sera mis au rebut.

0

En fait, l'exemple donné peut être être de type sécurisé. De plus, cela pourrait même être une bonne idée. Si plusieurs threads atteignent la valeur null avant que le premier thread ne soit écrit dans _instance (et même après que le premier thread ait été écrit dans _instance, mais avant que le CPU du second thread n'ait chargé la nouvelle valeur dans son cache), alors le deuxième (et troisième, et quatrième ...) thread créera une nouvelle instance et écrira ceci à _instance.

Dans un langage récupéré, cela signifie simplement que pendant un bref instant plusieurs threads auront leur propre version de _instance, mais bientôt ils sortiront de la portée et seront éligibles pour la récupération de place.

Maintenant, c'est un gaspillage, mais que ce soit réellement un problème ou non dépend de la coûteuse création d'une nouvelle instance et de l'existence de conséquences négatives s'il en existe plusieurs. Très souvent, l'inconvénient d'une telle duplication inutile des objets d'instance est assez faible, auquel cas il peut être moins négatif que le coût du verrouillage (le verrouillage est relativement bon marché mais pas gratuit, l'attente d'un verrou peut être assez coûteuse, et certains cas [impasse étant le cas le plus extrême] coûtant extrêmement cher), ou même de CASsing. Même si c'est plus cher, cela peut ne pas être dangereux.

Puisque nous ne pouvons pas juger si c'est le cas dans l'exemple ci-dessus, nous ne savons pas si c'est thread-safe ou non. Cependant, créer sur une construction statique est probablement la meilleure façon de procéder.

Questions connexes