Implémentation du modèle singleton C# et exemples de comparaison des performances

黄舟
Libérer: 2018-05-16 17:08:52
original
1979 Les gens l'ont consulté

Cet article présente principalement des informations pertinentes sur l'implémentation et la comparaison des performances du mode singleton C#. Il présente en détail 6 méthodes d'implémentation. Les amis dans le besoin peuvent se référer à l'

Introduction

Un singleton fait référence à une classe qui ne peut avoir qu'une seule instance (en C#, plus précisément, c'est une classe qui ne peut avoir qu'une seule instance dans chaque AppDomain. Elle est utilisée en génie logiciel comme l'un des modes les plus courants. Une fois que le premier utilisateur a créé une instance de cette classe, les utilisateurs suivants qui doivent utiliser cette classe ne peuvent utiliser que l'instance créée précédemment et ne peuvent pas créer une nouvelle instance. Un singleton est créé lors de sa première utilisation. plusieurs méthodes d'implémentation singleton en C# et analysez les différences de sécurité et de performances des threads entre elles

Il existe de nombreuses méthodes d'implémentation Singleton, mais à partir de l'implémentation la plus simple (non chargée paresseusement, non thread-safe et). inefficace) à une implémentation lente, thread-safe et efficace, elles ont toutes des points communs de base :

  • Les classes Singleton n'ont qu'un seul constructeur privé sans paramètre

  • La classe est déclarée scellée (non obligatoire)

  • Il y a une variable statique dans la classe qui contient une référence à l'instance créée

  • La classe singleton fournira une méthode ou une propriété statique pour renvoyer une référence à l'instance créée (par exemple.GetInstance)

Plusieurs implémentations

Un non-thread-safe

//Bad code! Do not use!
public sealed class Singleton
{
  private static Singleton instance = null;
  private Singleton()
  {
  }

  public static Singleton instance
  {
    get
    {
      if (instance == null)
      {
        instance = new Singleton();
      }
      return instance;
    }
  }
}
Copier après la connexion
Cette méthode n'est pas Thread-safe, il y aura deux threads s'exécutant if (instance == null) en même temps et créer deux instances différentes. La nouvelle instance créée remplacera celle nouvellement créée, ce qui rendra la référence précédemment obtenue vide >

Deuxième implémentation simple thread-safe

Par rapport à. première implémentation, cette version ajoute un verrou sur l'instance. Le cadenas doit être verrouillé avant d'appeler l'instance. Cela évite les conflits de threads dans la première implémentation. Cependant, puisque le verrou est utilisé à chaque fois. l'instance est appelée et le coût d'appel du verrou est important, cette implémentation entraînera une certaine perte de performances

public sealed class Singleton
{
  private static Singleton instance = null;
  private static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      lock (padlock)
      {
        if (instance == null)
        {
          instance = new Singleton();
        }
        return instance;
      }
    }
  }
}
Copier après la connexion
Notez qu'ici, nous utilisons un nouveau cadenas d'instance d'objet privé pour implémenter l'opération de verrouillage, au lieu de directement. verrouiller le Singleton. Il existe des risques potentiels à verrouiller directement le type, car ce type est public, donc en théorie, il peut être appelé dans n'importe quel code. Le verrouiller directement entraînera des problèmes de performances et même des blocages. : En C#, le même thread peut effectuer plusieurs opérations sur un objet. Il est verrouillé une fois, mais si différents threads sont verrouillés en même temps, une attente de thread peut se produire ou un blocage grave peut se produire. Par conséquent, lorsque nous utilisons le verrouillage, essayez de verrouiller les variables privées dans la classe afin d'éviter la situation ci-dessus.

Implémentation thread-safe de vérification triple

Tout en garantissant la sécurité des threads, cette implémentation évite également l'opération de verrouillage à chaque fois que l'instance est appelée, ce qui sera le cas gagner un certain temps. Cependant, cette implémentation a aussi ses inconvénients :

public sealed calss Singleton
{
  private static Singleton instance = null;
  private static readonly object padlock = new object();

  Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      if (instance == null)
      {
        lock (padlock)
        {
          if (instance == null)
          {
            instance = new Singleton();
          }
        }
      }
      return instance;
    }
  } 
}
Copier après la connexion
1 ne fonctionnera pas en Java. (Vous pouvez voir le texte original pour les raisons spécifiques, mais je ne comprends pas grand-chose ici)

2 Les programmeurs peuvent facilement commettre des erreurs lorsqu'ils l'implémentent eux-mêmes. Si vous apportez vos propres modifications au code dans ce mode, soyez très prudent car la logique de double vérification est relativement complexe et il est facile de commettre des erreurs dues à une mauvaise réflexion.


Quatre implémentations thread-safe sans verrous


Cette implémentation est très simple et n'utilise pas de verrous, mais elle est toujours thread-safe. Une instance Singleton statique en lecture seule est utilisée ici. Elle créera une nouvelle instance lorsque le Singleton est appelé pour la première fois. La garantie de sécurité des threads lors de la création d'une nouvelle instance est directement contrôlée par .NET. opération. Et il ne sera créé qu’une seule fois dans un AppDomaing.

Cette implémentation présente également quelques inconvénients :
public sealed class Singleton
{
  //在Singleton第一次被调用时会执行instance的初始化
  private static readonly Singleton instance = new Singleton();

  //Explicit static consturctor to tell C# compiler 
  //not to mark type as beforefieldinit
  static Singleton()
  {
  }

  private Singleton()
  {
  }

  public static Singleton Instance
  {
    get
    {
      return instance;
    }
  }
}
Copier après la connexion

1 Le moment de la création de l'instance est inconnu et tout appel à Singleton créera l'instance à l'avance

2 Appels en boucle du constructeur statique . S'il y a deux classes, A et B, et que B est appelé dans le constructeur statique de A et que A est appelé dans le constructeur statique de B, ces deux classes formeront un appel circulaire, ce qui entraînera sérieusement le crash du programme. 3 Nous devons ajouter manuellement le constructeur statique de Singleton pour nous assurer que le type Singleton ne sera pas automatiquement ajouté avec l'attribut beforefieldinit pour garantir que l'instance sera créée lorsque Singleton est appelé pour la première fois. 4L'attribut readonly ne peut pas être modifié au moment de l'exécution. Si nous devons supprimer l'instance et recréer une nouvelle instance lorsque le programme est en cours d'exécution, cette méthode d'implémentation ne peut pas être satisfaite.



Cinq instanciation entièrement paresseuse

L'implémentation cinq est un wrapper pour l'implémentation quatre. Cela garantit que l'instance ne sera appelée que dans la méthode get d'Instance et ne sera initialisée qu'avant le premier appel. Il s'agit d'une version de l'implémentation 4 qui assure un chargement paresseux.

Six utilise le type Lazy de .NET4
public sealed class Singleton
{
  private Singleton()
  {
  }

  public static Singleton Instance 
  {
    get
    {
      return Nested.instance;
    }
  }

  private class Nested
  {
    // Explicit static constructor to tell C# compiler
    // not to mark type as beforefieldinit
    static Nested()
    {
    }

    internal static readonly Singleton instance = new Singleton();
  }
}
Copier après la connexion

.NET4 ou supérieur prend en charge Lazy Le code garantit la sécurité des threads et les caractéristiques de chargement paresseux du singleton.

Différence de performances

Dans l'implémentation précédente, nous avons mis l'accent sur la sécurité des threads et le chargement paresseux du code. Cependant, en utilisation réelle, si l'initialisation de votre classe singleton ne prend pas de temps ou si la séquence d'initialisation ne provoque pas de bogues, l'initialisation retardée est une fonctionnalité superflue car le temps pris par l'initialisation est négligeable.

Dans les scénarios d'utilisation réels, si votre instance singleton est appelée fréquemment (comme dans une boucle), alors la consommation de performances causée par la garantie de la sécurité des threads mérite davantage d'attention.

Afin de comparer les performances de ces implémentations, j'ai fait un petit test, parcourant les singletons de ces implémentations 900 millions de fois, en appelant à chaque fois la méthode d'instance pour effectuer une opération count++, tous les millions. Sortie une fois , l'environnement d'exécution est Visual Studio pour Mac sur MBP. Les résultats sont les suivants :


线程安全性 延迟加载 测试运行时间(ms)
实现一 15532
实现二 45803
实现三 15953
实现四 不完全 14572
实现五 14295
实现六 22875

La méthode de test n'est pas rigoureuse, mais on voit quand même que la deuxième méthode est la plus longue car elle doit appeler lock à chaque fois, presque trois fois plus aussi longtemps que les autres. En deuxième position se trouve l'implémentation utilisant le type .NET Lazy, soit environ la moitié de plus que les autres. Les quatre autres ne présentent aucune différence évidente.

Résumé

En général, les différentes méthodes d'implémentation singleton mentionnées ci-dessus ne sont pas très différentes dans les performances informatiques actuelles, à moins que vous n'ayez besoin d'une concurrence particulièrement importante Seulement lorsque vous instance d’appel, vous devrez prendre en compte les problèmes de performances de verrouillage.

Pour les développeurs ordinaires, il suffit d'utiliser la méthode 2 ou la méthode 6 pour implémenter des singletons. Les méthodes 4 et 5 nécessitent une bonne compréhension du processus d'exécution et de l'implémentation en C#. Elles nécessitent certaines compétences et le temps qu'elles permettent de gagner. est encore limité.

Citation

La majeure partie de cet article est traduite de Implémentation du modèle Singleton en C#, avec une partie de ma propre compréhension ajoutée. C'est ce que j'ai vu lorsque j'ai recherché l'initialiseur de champ statique en lecture seule par rapport à l'initialisation du constructeur statique. Je voudrais exprimer mes remerciements aux deux auteurs ici.

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Étiquettes associées:
source:php.cn
Déclaration de ce site Web
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn
Tutoriels populaires
Plus>
Derniers téléchargements
Plus>
effets Web
Code source du site Web
Matériel du site Web
Modèle frontal
À propos de nous Clause de non-responsabilité Sitemap
Site Web PHP chinois:Formation PHP en ligne sur le bien-être public,Aidez les apprenants PHP à grandir rapidement!