Singleton Thread-Safe
Fait partie de la section Programmation Orientée Objet du Journey C# de Coddy — leçon 52 sur 70.
Le pattern Singleton garantit qu'une classe n'a qu'une seule instance dans toute votre application et fournit un point d'accès global à celle-ci. Ceci est utile pour gérer des ressources partagées telles que les paramètres de configuration, les services de journalisation ou les connexions aux bases de données.
Un Singleton de base utilise un constructeur privé pour empêcher l'instanciation externe et une propriété statique pour fournir l'accès :
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // Constructeur privé
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}L'instruction lock garantit la sécurité des threads (thread safety) - si plusieurs threads tentent d'accéder à Instance simultanément, un seul peut entrer dans la section critique à la fois. Sans cela, deux threads pourraient tous deux voir _instance comme étant nul et créer des instances distinctes.
C# propose une approche thread-safe plus simple utilisant l'initialisation statique :
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}Cette version est thread-safe car le CLR garantit que l'initialisation des champs statiques ne se produit qu'une seule fois. Les deux approches garantissent que vous obtenez toujours la même instance :
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");Défi
FacileConstruisons un gestionnaire de configuration thread-safe en utilisant le pattern Singleton. Dans les applications réelles, les paramètres de configuration doivent être accessibles dans tout le programme tout en garantissant qu'une seule instance existe - un cas d'utilisation parfait pour le Singleton.
Vous organiserez votre code sur deux fichiers :
ConfigManager.cs: Créez une classeConfigManagerdans l'espace de nomsConfigurationqui implémente le pattern Singleton. Votre gestionnaire de configuration doit :- Avoir un champ statique privé pour contenir l'instance unique
- Utiliser un constructeur privé pour empêcher l'instanciation externe
- Fournir une propriété statique
Instancequi retourne l'instance unique (utilisez l'approche d'initialisation statique plus simple pour la sécurité des threads) - Inclure un champ de chaîne privé
_appNameinitialisé à"MyApp" - Avoir une méthode
SetAppName(string name)pour mettre à jour le nom de l'application - Avoir une méthode
GetAppName()qui retourne le nom actuel de l'application
Program.cs: Dans votre fichier principal, démontrez que le Singleton fonctionne correctement en accédant à l'instance plusieurs fois et en montrant que les modifications persistent à travers toutes les références.
Vous recevrez une entrée :
- Un nouveau nom d'application à définir (par exemple,
ProductionServer)
Dans votre programme :
- Récupérez l'instance de
ConfigManageret affichez le nom de l'application par défaut - Récupérez à nouveau l'instance (dans une variable différente) et utilisez-la pour définir le nouveau nom de l'application à partir de l'entrée
- Récupérez l'instance une troisième fois et affichez le nom de l'application pour prouver que toutes les références partagent les mêmes données
Par exemple, si l'entrée est ProductionServer, la sortie devrait être :
MyApp
ProductionServerCela démontre l'avantage principal du Singleton - peu importe le nombre de fois que vous accédez à Instance, vous obtenez toujours le même objet avec un état partagé. L'approche d'initialisation statique garantit la sécurité des threads car le CLR s'assure que le champ statique n'est initialisé qu'une seule fois, même si plusieurs threads y accèdent simultanément.
Aide-mémoire
Le pattern Singleton garantit qu'une classe n'a qu'une seule instance dans toute votre application et fournit un point d'accès global à celle-ci.
Singleton de base avec verrouillage explicite pour la sécurité des threads (thread safety) :
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // Constructeur privé
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}L'instruction lock garantit la sécurité des threads en ne permettant qu'à un seul thread d'entrer dans la section critique à la fois.
Singleton thread-safe plus simple utilisant l'initialisation statique :
public class Logger
{
private static readonly Logger _instance = new Logger();
private Logger() { }
public static Logger Instance => _instance;
public void Log(string message) => Console.WriteLine(message);
}Cette approche est thread-safe car le CLR garantit que l'initialisation des champs statiques ne se produit qu'une seule fois.
Utilisation - retourne toujours la même instance :
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");Essayez vous-même
using System;
using Configuration;
class Program
{
public static void Main(string[] args)
{
// Lire l'entrée - le nouveau nom de l'application
string newAppName = Console.ReadLine();
// TODO: Obtenir l'instance de ConfigManager et afficher le nom de l'application par défaut
// TODO: Obtenir à nouveau l'instance (dans une variable différente) et l'utiliser pour définir le nouveau nom de l'application
// TODO: Obtenir l'instance une troisième fois et afficher le nom de l'application pour prouver que toutes les références partagent les mêmes données
}
}
Cette leçon comprend un petit quiz. Commencez la leçon pour y répondre et suivre votre progression.
Toutes les leçons de Programmation Orientée Objet
1Fondamentaux de la POO
Fichiers externesEspaces de noms et directivesIntro aux classes et objetsLe mot-clé 'this'Méthodes et paramètresChamps vs PropriétésConstructeursInitialiseurs d'objetsRécapitulatif - Calculatrice simple4Héritage
Syntaxe de base de l'héritage (:)Le mot-clé 'base'Mots-clés Virtual & OverrideClasses scelléesLa classe de base 'object'Récapitulatif - Hiérarchie des employés7Fonctionnalités avancées
Surcharge d'opérateursIndexeurs (this[])Redéfinition de ToString()Méthodes d'extensionRécapitulatif - Liste personnalisée10Patrons de conception - Partie 1
Introduction aux patrons de conceptionSingleton Thread-SafePatron FabriquePatron Observateur (Événements)Patron Stratégie2Propriétés et membres statiques
Propriétés auto-implémentéesPropriétés en lecture/écriture seuleChamps et méthodes statiquesClasses statiquesMembres à corps d'expression5Polymorphisme & Interfaces
Polymorphisme : Compilation vs ExécutionInterface vs Classe AbstraiteInterfaces MultiplesInterfaces ExplicitesUpcasting & DowncastingRécapitulatif - Calculateur de Formes8Concepts avancés de la POO
Composition plutôt qu'héritageGénériques (Classes et Méthodes)Délégués et ÉvénementsAttributs et RéflexionIDisposable et l'instruction usingBases de l'Injection de Dépendances11Patrons de conception, Partie 2
Patron CommandePatron AdaptateurPatron DécorateurPatron Méthode TemplatePatron ÉtatPatron Composite3Architecture des classes
Données d'instance vs statiquesMots-clés 'readonly' & 'const'Champs de supportRécapitulatif - Gestionnaire de compte bancaire6Encapsulation
Modificateurs d'accèsPropriétés pour l'encapsulationImplémentation du masquage de donnéesPatterns d'immuabilitéRécapitulatif - Dossiers d'étudiants12Projet : Système de gestion de bibliothèque
Structure du projetModèles Livre et Utilisateur