Maison > Article > développement back-end > Implémentation du modèle singleton C# et exemples de comparaison des performances
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 :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; } } }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; } } } }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; } } }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.
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; } } }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 Lazy8742468051c85b06f0a0af9e3e506b5c de .NET4public 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(); } }
.NET4 ou supérieur prend en charge Lazy8742468051c85b06f0a0af9e3e506b5c 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 : 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.
线程安全性
延迟加载
测试运行时间(ms)
实现一
否
是
15532
实现二
是
是
45803
实现三
是
是
15953
实现四
是
不完全
14572
实现五
是
是
14295
实现六
是
是
22875
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!