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 asegura que una clase tenga una única instancia en toda la 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 evitar 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 sentencia lock garantiza la seguridad de hilos (thread safety): si varios hilos intentan acceder a Instance simultáneamente, solo uno puede entrar en la sección crítica a la vez. Sin esto, dos hilos podrían ver _instance como nulo y crear instancias separadas.

C# ofrece un enfoque más sencillo y seguro para hilos (thread-safe) utilizando 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 el CLR garantiza que la inicialización de campos estáticos ocurra solo una vez. Ambos enfoques aseguran que siempre obtengas la misma instancia:

Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");
challenge icon

Desafío

Fácil

Vamos a construir un gestor de configuración seguro para hilos (thread-safe) utilizando el patrón Singleton. En las aplicaciones reales, los ajustes de configuración deben ser accesibles en todo el programa, garantizando al mismo tiempo que solo exista una instancia; un caso de uso perfecto para el Singleton.

Organizarás tu código en dos archivos:

  • ConfigManager.cs: Crea una clase ConfigManager en el espacio de nombres Configuration que implemente el patrón Singleton. Tu gestor de configuración debe:
    • Tener un campo estático privado para mantener la instancia única
    • Usar un constructor privado para evitar la instanciación externa
    • Proporcionar una propiedad estática Instance que devuelva la instancia única (utiliza el enfoque de inicialización estática más simple para la seguridad de hilos)
    • Incluir un campo de cadena privado _appName inicializado como "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 el 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 para configurar (por ejemplo, ProductionServer)

En tu programa:

  1. Obtén la instancia de ConfigManager e imprime el nombre de la aplicación por defecto
  2. Obtén la instancia de nuevo (en una variable diferente) y úsala para establecer el nuevo nombre de la aplicación desde 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 debería ser:

MyApp
ProductionServer

Esto demuestra el beneficio principal del Singleton: no importa cuántas veces accedas a Instance, siempre obtendrás el mismo objeto con estado compartido. El enfoque de inicialización estática garantiza la seguridad de hilos porque el CLR asegura que el campo estático se inicialice solo una vez, incluso si varios hilos acceden a él simultáneamente.

Hoja de referencia

El patrón Singleton garantiza que una clase tenga una sola instancia en toda la aplicación y proporciona un punto de acceso global a ella.

Singleton básico con bloqueo explícito para seguridad de hilos (thread safety):

public class Logger
{
    private static Logger _instance;
    private static readonly object _lock = new object();
    
    private Logger() { }  // Private constructor
    
    public static Logger Instance
    {
        get
        {
            lock (_lock)
            {
                if (_instance == null)
                    _instance = new Logger();
                return _instance;
            }
        }
    }
    
    public void Log(string message) => Console.WriteLine(message);
}

La sentencia lock garantiza la seguridad de hilos al permitir que solo un hilo entre en la sección crítica a la vez.

Singleton seguro para hilos más simple utilizando 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);
}

Este enfoque es seguro para hilos porque el CLR garantiza que la inicialización de los campos estáticos ocurra solo una vez.

Uso - siempre devuelve la misma instancia:

Logger.Instance.Log("First call");
Logger.Instance.Log("Same instance");

Pruébalo tú mismo

using System;
using Configuration;

class Program
{
    public static void Main(string[] args)
    {
        // Leer 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