Menu
Coddy logo textTech

Singleton thread-safe

Fa parte della sezione Programmazione orientata agli oggetti del percorso C# di Coddy. Lezione 52 di 70.

Il pattern Singleton garantisce che una classe abbia una sola istanza nell’intera applicazione e fornisce un punto di accesso globale a essa. È utile per gestire risorse condivise come le impostazioni di configurazione, i servizi di registrazione o le connessioni al database.

Un Singleton di base usa un costruttore privato per impedire l'istanziazione esterna e una proprietà statica per fornire l'accesso:

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

L'istruzione lock garantisce la sicurezza dei thread: se più thread provano ad accedere a Instance contemporaneamente, solo uno alla volta può entrare nella sezione critica. Senza questa istruzione, due thread potrebbero vedere entrambi _instance come null e creare istanze separate.

C# offre un approccio più semplice e thread-safe tramite l'inizializzazione statica:

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);
}

Questa versione è thread-safe perché il CLR garantisce che l’inizializzazione dei campi statici avvenga una sola volta. Entrambi gli approcci garantiscono che tu ottenga sempre la stessa istanza:

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

Sfida

Facile

Creiamo un gestore della configurazione thread-safe usando il pattern Singleton. Nelle applicazioni reali, le impostazioni di configurazione devono essere accessibili in tutto il programma, assicurando al contempo che esista una sola istanza: un caso d’uso perfetto per Singleton.

Organizzerai il codice in due file:

  • ConfigManager.cs: crea una classe ConfigManager nello spazio dei nomi Configuration che implementa il pattern Singleton. Il gestore della configurazione deve:
    • Avere un campo statico privato che contenga l’unica istanza
    • Usare un costruttore privato per impedire la creazione di istanze dall’esterno
    • Fornire una proprietà statica Instance che restituisca l’unica istanza (usa l’approccio più semplice di inizializzazione statica per garantire la sicurezza dei thread)
    • Includere un campo stringa privato _appName inizializzato a "MyApp"
    • Avere un metodo SetAppName(string name) per aggiornare il nome dell’app
    • Avere un metodo GetAppName() che restituisca il nome attuale dell’app
  • Program.cs: nel file principale, dimostra che Singleton funziona correttamente accedendo più volte all’istanza e mostrando che le modifiche persistono in tutti i riferimenti.

Riceverai un input:

  • Un nuovo nome dell’applicazione da impostare (ad es. ProductionServer)

Nel tuo programma:

  1. Ottieni l’istanza di ConfigManager e stampa il nome predefinito dell’app
  2. Ottieni di nuovo l’istanza (assegnandola a una variabile diversa) e usala per impostare il nuovo nome dell’app ricevuto in input
  3. Ottieni l’istanza una terza volta e stampa il nome dell’app per dimostrare che tutti i riferimenti condividono gli stessi dati

Ad esempio, se l’input è ProductionServer, l’output dovrebbe essere:

MyApp
ProductionServer

Questo dimostra il vantaggio fondamentale di Singleton: non importa quante volte accedi a Instance, ottieni sempre lo stesso oggetto con uno stato condiviso. L’approccio di inizializzazione statica garantisce la sicurezza dei thread perché il CLR assicura che il campo statico venga inizializzato una sola volta, anche se più thread vi accedono simultaneamente.

Provalo tu

using System;
using Configuration;

class Program
{
    public static void Main(string[] args)
    {
        // Leggi l'input - il nuovo nome dell'applicazione
        string newAppName = Console.ReadLine();
        
        // TODO: Ottieni l'istanza di ConfigManager e stampa il nome predefinito dell'applicazione
        
        // TODO: Ottieni di nuovo l'istanza (in una variabile diversa) e usala per impostare il nuovo nome dell'applicazione
        
        // TODO: Ottieni l'istanza una terza volta e stampa il nome dell'applicazione per dimostrare che tutti i riferimenti condividono gli stessi dati
    }
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C# online