99 votes

Modèle de singleton C# sans threads

J'ai quelques questions concernant le modèle singleton tel que documenté ici : http://msdn.microsoft.com/en-us/library/ff650316.aspx

Le code suivant est un extrait de l'article :

using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}

Plus précisément, dans l'exemple ci-dessus, est-il nécessaire de comparer l'instance à null deux fois, avant et après le verrouillage ? Est-ce nécessaire ? Pourquoi ne pas effectuer le verrouillage d'abord et faire la comparaison ensuite ?

Y a-t-il un problème à simplifier à ce qui suit ?

   public static Singleton Instance
   {
      get 
      {
        lock (syncRoot) 
        {
           if (instance == null) 
              instance = new Singleton();
        }

         return instance;
      }
   }

L'exécution de la serrure est-elle coûteuse ?

149voto

Jon Points 194296

L'exécution de la serrure est terriblement coûteux par rapport à la simple vérification du pointeur instance != null .

Le modèle que vous voyez ici s'appelle verrouillage doublement vérifié . Son but est d'éviter la coûteuse opération de verrouillage qui ne sera nécessaire qu'une seule fois (lors du premier accès au singleton). L'implémentation est telle qu'elle doit également garantir que lorsque le singleton est initialisé, il n'y aura pas de bogues résultant de conditions de course de threads.

Pensez-y de cette façon : une personne nue null (sans lock ) est garanti de vous donner une réponse correcte et utilisable uniquement lorsque cette réponse est "oui, l'objet est déjà construit". Mais si la réponse est "pas encore construit", alors vous n'avez pas assez d'informations car ce que vous vouliez vraiment savoir, c'est qu'il n'est "pas encore construit". et aucun autre fil de discussion n'a l'intention de le construire prochainement. ". Vous utilisez donc la vérification externe comme un test initial très rapide et vous lancez la procédure appropriée, sans bogue mais "coûteuse" (verrouillage puis vérification) uniquement si la réponse est "non".

L'implémentation ci-dessus est assez bonne pour la plupart des cas, mais à ce stade, il est bon de lire L'article de Jon Skeet sur les singletons en C# qui évalue également d'autres alternatives.

45voto

andasa Points 451

En Lazy<T> version :

public sealed class Singleton
{
    private static readonly Lazy<Singleton> lazy
        = new Lazy<Singleton>(() => new Singleton());

    public static Singleton Instance
        => lazy.Value;

    private Singleton() { }
}

Nécessite .NET 4 et C# 6.0 (VS2015) ou une version plus récente.

16voto

Jon Hanna Points 40291

Effectuer un verrouillage : Assez bon marché (toujours plus cher qu'un test nul).

Exécuter un verrou lorsqu'un autre thread le possède : Vous obtenez le coût de tout ce qu'ils ont encore à faire pendant le verrouillage, ajouté à votre propre temps.

Exécuter un verrou lorsqu'un autre thread l'a, et que des dizaines d'autres threads l'attendent également : Paralysant.

Pour des raisons de performance, vous voulez toujours avoir des verrous qu'un autre thread veut, pour la plus courte période de temps possible.

Bien sûr, il est plus facile de raisonner sur des verrous "larges" que sur des verrous étroits, et il vaut donc la peine de commencer par des verrous larges et d'optimiser si nécessaire, mais il y a certains cas que nous apprenons par l'expérience et la familiarité et pour lesquels un verrou plus étroit correspond au modèle.

(Par ailleurs, si vous pouvez utiliser simplement l'option private static volatile Singleton instance = new Singleton() ou si vous pouvez éviter d'utiliser les singletons et utiliser une classe statique à la place, les deux sont meilleurs en ce qui concerne ces préoccupations).

8voto

Heinzi Points 66519

La raison en est la performance. Si instance != null (ce qui sera toujours le cas, sauf la toute première fois), il n'est pas nécessaire de procéder à un coûteux lock : Deux threads accédant simultanément au singleton initialisé seraient synchronisés inutilement.

4voto

KooKiz Points 19056

Dans presque tous les cas (c'est-à-dire tous les cas sauf les tout premiers), instance ne sera pas nulle. Acquérir un verrou est plus coûteux qu'une simple vérification, donc vérifier une fois la valeur de instance avant le verrouillage est une optimisation agréable et gratuite.

Ce modèle est appelé verrouillage à double vérification : http://en.wikipedia.org/wiki/Double-checked_locking

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X