Menu
Coddy logo textTech

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");
challenge icon

Desafío

Fácil

Construyamos 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 clase ConfigManager en el espacio de nombres Configuration que 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 Instance que 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 _appName inicializado 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:

  1. Obtén la instancia de ConfigManager e imprime el nombre predeterminado de la aplicación
  2. Obtén la instancia nuevamente (en una variable diferente) y úsala para establecer el nuevo nombre de aplicación de la entrada
  3. 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
ProductionServer

Esto 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
    }
}
quiz iconPonte a prueba

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

Practica por tu cuenta: Compilador de C# online