Singleton Pattern

written by devangelist on Juli 28, 2012 in ASP.NET and Patterns with no comments

Das Singleton-Pattern ist vom Prinzip her sehr einfach: Es gibt nur eine Instanz einer Klasse. Allerdings gibt es mehrere Möglichkeiten dieses Pattern zu implementieren und diese unterscheiden sich vor allem bei der Erzeugung der Instanz.
Als erster Schritt muss sichergestellt werden, dass die Klasse nicht zweimal erzeugt werden kann. Das bedeutet in erster Linie, dass nur die Singleton-Klasse selbst eine Instanz erzeugen darf. Diese Einschränkung kann über einen privaten Konstruktor schnell erreicht werden:

public class Singleton
{
   private Singleton() {}

   public static Singleton GetInstance()
   {
      return new Singleton();
   }
}

Die Klasse kann nun nicht mehr von anderen Klassen erzeugt werden sondern nur mehr von sich selbst. Standard ist eine Funktion GetInstance() bzw. eine readonly Property Instance() welche die Instanz zurückgeben soll.
Das Problem des doppelten Erzeugens ist aber weiterhin vorhanden, sprich bei jedem Aufruf der Funktion wird eine neue Instanz der Klasse erzeugt. Auch für dieses Problem gibt es eine einfache Lösung nämlich Lazy-Loading – das heißt die Instanz wird erst erzeugt wenn sie tatsächlich verwendet wird.

public class Singleton
{
   private static Singleton instance;

   private Singleton() {}

   public static Singleton Instance
   {
      get
      {
         if (instance == null)
         {
            instance = new Singleton();
         }
         return instance;
      }
   }
}

Rein technisch würde das gezeigte Beispiel funktionieren. Und auch aus praktischer Hinsicht, jedenfalls zum Teil, denn es gibt eine große Schwachstelle dieser Singleton-Implementierung welche die Thread-Sicherheit betrifft. Wenn es sich um eine Anwendung handelt, die auf einem Multicore-Rechner läuft bzw. von Multithreading gebraucht macht besteht die Gefahr, dass die Instanz mehrfach erzeugt wird, denn:
Angenommen zwei Prozesse möchten gleichzeitig die Instanz erhalten (Zeile13) welche allerdings noch nicht existiert, dann würde diese von beiden Prozessen erzeugt werden, denn die Variable instance wäre bei beiden noch null.
Um den Singleton den gewünschten, threadsicheren Touch zu geben gibt es zwei Möglichkeiten:

Statische Initialisierung

public sealed class Singleton
{
   private static readonly Singleton instance = new Singleton();

   private Singleton(){}

   public static Singleton Instance
   {
      get
      {
         return instance;
      }
   }
}

Bei der statischen Variante wird die Instanz direkt erzeugt, sprich die Möglichkeit, dass das Objekt den Wert null haben kann, entfällt komplett. Nachteil dabei ist die fehlende Lazy-Implementierung, wenn also der Singleton ein relativ komplexes Objekt ist welches nicht immer verwendet wird, entsteht dabei eine zusätzliche Last.

Der Threadsafe Singleton

Die in dieser Variante verwendete Technik ist auch bekannt als Double-checked Locking. Das Lock verhindert das doppelte Erstellen der Instanz, sprich es können ab dem lock (lockObject) keine weiteren Threads darauf zugreifen bis das Locking beendet wurde. Das double wäre in dem Fall eine zweifache Überprüfung ob die Instanz bereits erstellt wurde. Diese doppelte Überprüfung hat den Hintergrund, dass das locken von Objekten sehr performancelastig sein kann und somit nur im ersten Durchlauf gemacht wird.

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object lockObject = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get
      {
         if (instance == null)
         {
            lock (lockObject)
            {
               if (instance == null)
                  instance = new Singleton();
            }
         }
         return instance;
      }
   }
}

Wer jetzt einen Schritt weiter denkt wird sich fragen, warum für jede Implementierung derselbe Code geschrieben wird. Ziemlich doof auch, denn dabei wird das DRY (dont repeat yourself) Prinzip verletzt. .NET sei Dank gibt es auch hierfür eine einfache Erweiterung, nämlich die generischen Klassen.

Der generische Singleton

public sealed class Singleton<T>
{
   private static volatile Singleton<T> instance;
   private static object lockObject = new Object();

   private Singleton() {}

   public static Singleton<T> Instance
   {
      get
      {
         if (instance == null)
         {
            lock (lockObject)
            {
               if (instance == null)
                  instance = new Singleton<T>();
            }
         }
         return instance;
      }
   }
}

Der einzige Unterschied zum Threadsafe Singleton ist der generische Typparameter T, welcher den Singleton auch als Namen logisch macht.

var factory = Singleton.Instance();

Wer Wert auf Details legt und sich den Code genauer angeschaut hat, dem wird sicherlich das volatile Keyword aufgefallen sein. Einfach gesagt würde dieses eine Compiler-Optimierung verhindern, warum das genau in dieser Implementierung Sinn macht und mehr dazu im nächsten Post!

Singleton Pattern: 1 Stern2 Sterne3 Sterne4 Sterne5 Sterne 4,60 von 5 Punkte, 5 abgegebene Stimmen.
Loading ... Loading ...

About the Author

Roberto Bez ist passionierter Webentwickler und TechLead bei der HolidayCheck AG. Für Roberto bedeutet das Entwickeln nicht nur Arbeit, sondern auch Freude, Motivation und täglich neue, aufregende Herausforderungen. Besonders gerne setzt er sich mit neuen Webtechnologien sowie Datenbanken aller Art auseinander und versucht diese in die tägliche Anwendungsentwicklung miteinzubringen. Neben dem Entwickeln trifft man ihn gerne Abends beim Laufen oder im Sommer bei Mountainbike-Touren durch die schönen Berge Südtirols.