Singleton Thread-Safe
Parte de la sección Programación Orientada a Objetos del Journey de C# de Coddy. Lección 52 de 70.
El patrón Singleton garantiza que una clase tenga una sola instancia en toda su aplicación y proporciona un punto de acceso global a ella. Esto es útil para gestionar recursos compartidos como ajustes de configuración, servicios de registro o conexiones a bases de datos.
Un Singleton básico utiliza un constructor privado para impedir la instanciación externa y una propiedad estática para proporcionar acceso:
public class Logger
{
private static Logger _instance;
private static readonly object _lock = new object();
private Logger() { } // Constructor privado
public static Logger Instance
{
get
{
lock (_lock)
{
if (_instance == null)
_instance = new Logger();
return _instance;
}
}
}
public void Log(string message) => Console.WriteLine(message);
}La instrucción lock garantiza la seguridad de los subprocesos: si varios subprocesos intentan acceder a Instance simultáneamente, solo uno puede entrar en la sección crítica a la vez. Sin esto, dos subprocesos podrían ver ambos que _instance es null y crear instancias independientes.
C# ofrece un enfoque más sencillo y seguro para subprocesos mediante la inicialización estática:
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);
}Esta versión es segura para subprocesos porque CLR garantiza que la inicialización del campo estático ocurre solo una vez. Ambos enfoques garantizan que siempre obtengas la misma instancia:
Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");Desafío
FácilConstruyamos un administrador de configuración seguro para subprocesos mediante el patrón Singleton. En las aplicaciones reales, la configuración debe ser accesible desde todo el programa, garantizando al mismo tiempo que exista una sola instancia: un caso de uso perfecto para Singleton.
Organizarás el código en dos archivos:
ConfigManager.cs: Crea una claseConfigManageren el espacio de nombresConfigurationque implemente el patrón Singleton. Tu administrador de configuración debe:- Tener un campo estático privado para almacenar la única instancia
- Usar un constructor privado para impedir la creación de instancias externas
- Proporcionar una propiedad estática
Instanceque devuelva la única instancia (usa el enfoque más sencillo de inicialización estática para garantizar la seguridad de los subprocesos) - Incluir un campo privado de tipo string
_appNameinicializado con"MyApp" - Tener un método
SetAppName(string name)para actualizar el nombre de la aplicación - Tener un método
GetAppName()que devuelva el nombre actual de la aplicación
Program.cs: En tu archivo principal, demuestra que Singleton funciona correctamente accediendo a la instancia varias veces y mostrando que los cambios persisten en todas las referencias.
Recibirás una entrada:
- Un nuevo nombre de aplicación que establecer (por ejemplo,
ProductionServer)
En tu programa:
- Obtén la instancia de
ConfigManagere imprime el nombre predeterminado de la aplicación - Obtén la instancia nuevamente (en una variable diferente) y úsala para establecer el nuevo nombre de aplicación de la entrada
- Obtén la instancia por tercera vez e imprime el nombre de la aplicación para demostrar que todas las referencias comparten los mismos datos
Por ejemplo, si la entrada es ProductionServer, la salida debe ser:
MyApp
ProductionServerEsto demuestra el beneficio principal de Singleton: sin importar cuántas veces accedas a Instance, siempre obtendrás el mismo objeto con un estado compartido. El enfoque de inicialización estática garantiza la seguridad de los subprocesos porque CLR garantiza que el campo estático se inicialice solo una vez, incluso si varios subprocesos acceden a él simultáneamente.
Pruébalo tú mismo
using System;
using Configuration;
class Program
{
public static void Main(string[] args)
{
// Leer la entrada - el nuevo nombre de la aplicación
string newAppName = Console.ReadLine();
// TODO: Obtener la instancia de ConfigManager e imprimir el nombre de la aplicación por defecto
// TODO: Obtener la instancia de nuevo (en una variable diferente) y usarla para establecer el nuevo nombre de la aplicación
// TODO: Obtener la instancia por tercera vez e imprimir el nombre de la aplicación para demostrar que todas las referencias comparten los mismos datos
}
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosNamespaces y directivasIntro a clases y objetosLa palabra clave 'this'Métodos y parámetrosCampos vs PropiedadesConstructoresInicializadores de objetosResumen - Calculadora simple4Herencia
Sintaxis básica de herencia (:)La palabra clave 'base'Palabras clave Virtual y OverrideClases selladas (Sealed)La clase base 'object'Resumen: Jerarquía de empleados7Características avanzadas
Sobrecarga de operadoresIndexadores (this[])Sobrescritura de ToString()Métodos de extensiónResumen - Lista personalizada10Patrones de diseño - Parte 1
Introducción a los patrones de diseñoSingleton Thread-SafePatrón FactoryPatrón Observer (Eventos)Patrón Strategy2Propiedades y miembros estáticos
Propiedades autoimplementadasPropiedades de solo lectura y escrituraCampos y métodos estáticosClases estáticasMiembros con cuerpo de expresión5Polimorfismo e Interfaces
Polimorfismo: Compilación vs. EjecuciónInterfaz vs. Clase AbstractaInterfaces MúltiplesInterfaces ExplícitasUpcasting y DowncastingRepaso: Calculadora de Figuras8Conceptos avanzados de POO
Composición sobre herenciaGenéricos (clases y métodos)Delegados y eventosAtributos y ReflexiónIDisposable y la sentencia usingFundamentos de Inyección de dependencias11Patrones de diseño, parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón Composite3Arquitectura de clases
Datos de instancia vs. estáticosPalabras clave 'readonly' y 'const'Campos de respaldoResumen - Gestor de cuentas bancarias6Encapsulamiento
Modificadores de accesoPropiedades para el encapsulamientoImplementación de ocultamiento de datosPatrones de inmutabilidadRepaso - Registros de estudiantesPractica por tu cuenta: Compilador de C# online