Menu
Coddy logo textTech

Thread-sicheres Singleton

Teil des Abschnitts Objektorientierte Programmierung der C#-Journey von Coddy. Lektion 52 von 70.

Das Singleton-Muster stellt sicher, dass eine Klasse während der gesamten Anwendung nur eine einzige Instanz hat, und bietet einen globalen Zugriffspunkt darauf. Dies ist nützlich für die Verwaltung gemeinsam genutzter Ressourcen wie Konfigurationseinstellungen, Protokollierungsdienste oder Datenbankverbindungen.

Ein einfaches Singleton verwendet einen privaten Konstruktor, um eine externe Instanziierung zu verhindern, und eine statische Eigenschaft, um den Zugriff zu ermöglichen:

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

Die lock-Anweisung gewährleistet Threadsicherheit – wenn mehrere Threads gleichzeitig versuchen, auf Instance zuzugreifen, kann immer nur einer gleichzeitig den kritischen Abschnitt betreten. Ohne dies könnten zwei Threads _instance beide als null sehen und separate Instanzen erstellen.

C# bietet einen einfacheren threadsicheren Ansatz unter Verwendung der statischen Initialisierung:

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

Diese Version ist threadsicher, da die CLR garantiert, dass die Initialisierung statischer Felder nur einmal erfolgt. Beide Ansätze stellen sicher, dass Sie immer dieselbe Instanz erhalten:

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

Aufgabe

Einfach

Erstellen wir einen threadsicheren Konfigurationsmanager unter Verwendung des Singleton-Musters. In realen Anwendungen müssen Konfigurationseinstellungen im gesamten Programm zugänglich sein, wobei gleichzeitig sichergestellt wird, dass nur eine Instanz existiert – ein perfekter Anwendungsfall für Singleton.

Du wirst deinen Code auf zwei Dateien aufteilen:

  • ConfigManager.cs: Erstelle eine ConfigManager-Klasse im Namespace Configuration, die das Singleton-Muster implementiert. Dein Konfigurationsmanager sollte:
    • Ein privates statisches Feld besitzen, das die einzige Instanz speichert
    • Einen privaten Konstruktor verwenden, um eine externe Instanziierung zu verhindern
    • Eine statische Instance-Eigenschaft bereitstellen, die die einzige Instanz zurückgibt (verwende zur Threadsicherheit den einfacheren Ansatz der statischen Initialisierung)
    • Ein privates String-Feld _appName enthalten, das mit "MyApp" initialisiert wird
    • Eine SetAppName(string name)-Methode besitzen, um den App-Namen zu aktualisieren
    • Eine GetAppName()-Methode besitzen, die den aktuellen App-Namen zurückgibt
  • Program.cs: Zeige in deiner Hauptdatei, dass das Singleton korrekt funktioniert, indem du mehrmals auf die Instanz zugreifst und zeigst, dass Änderungen über alle Verweise hinweg erhalten bleiben.

Du erhältst eine Eingabe:

  • Einen neuen App-Namen, der festgelegt werden soll (z. B. ProductionServer)

In deinem Programm:

  1. Rufe die ConfigManager-Instanz ab und gib den Standard-App-Namen aus
  2. Rufe die Instanz erneut ab (in einer anderen Variablen) und verwende sie, um den neuen App-Namen aus der Eingabe festzulegen
  3. Rufe die Instanz ein drittes Mal ab und gib den App-Namen aus, um zu beweisen, dass alle Verweise dieselben Daten gemeinsam nutzen

Wenn die Eingabe beispielsweise ProductionServer lautet, sollte die Ausgabe wie folgt aussehen:

MyApp
ProductionServer

Dies veranschaulicht den zentralen Vorteil von Singleton: Unabhängig davon, wie oft du auf Instance zugreifst, erhältst du immer dasselbe Objekt mit gemeinsam genutztem Zustand. Der Ansatz der statischen Initialisierung garantiert Threadsicherheit, da die CLR sicherstellt, dass das statische Feld nur einmal initialisiert wird, selbst wenn mehrere Threads gleichzeitig darauf zugreifen.

Probier es selbst

using System;
using Configuration;

class Program
{
    public static void Main(string[] args)
    {
        // Eingabe lesen - der neue Anwendungsname
        string newAppName = Console.ReadLine();
        
        // TODO: Die ConfigManager-Instanz abrufen und den Standard-App-Namen ausgeben
        
        // TODO: Die Instanz erneut abrufen (in eine andere Variable) und sie verwenden, um den neuen App-Namen festzulegen
        
        // TODO: Die Instanz ein drittes Mal abrufen und den App-Namen ausgeben, um zu beweisen, dass alle Referenzen dieselben Daten teilen
    }
}
quiz iconTeste dich selbst

Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.

Alle Lektionen in Objektorientierte Programmierung

Übe selbstständig: Online-C#-Compiler