Eine einfache Implementierung eines Singleton

8

Ist das nicht ein einfacherer und sicherer (und daher besserer) Weg, ein Singleton zu implementieren, anstatt doppeltes Locking von mambo-jambo zu machen? Irgendwelche Nachteile dieses Ansatzes?

%Vor%

BEARBEITEN: Der Test auf Thread-Sicherheit ist fehlgeschlagen, kann jemand erklären warum? Wie kommt es, dass Interlocked.CompareExchange nicht wirklich atomar ist?

%Vor%     
kateroh 13.05.2011, 19:20
quelle

13 Antworten

8

Sie erstellen zwar möglicherweise mehrere Instanzen, aber diese werden mit Garbage Collection gesammelt, da sie nirgends verwendet werden. In keinem Fall ändert die statische _instance-Feldvariable ihren Wert mehr als einmal, der einzige Zeitpunkt, zu dem sie von null auf einen gültigen Wert wechselt. Daher werden Konsumenten dieses Codes immer dieselbe Instanz sehen, obwohl mehrere Instanzen erstellt wurden.

Sperren Sie die freie Programmierung

Joe Duffy , analysiert dies in seinem Buch Concurrent Programming on Windows Sehr Muster, das Sie versuchen, in Kapitel 10, Speichermodelle und Lock Freedom, Seite 526 zu verwenden.

Er bezeichnet dieses Muster als Lazy-Initialisierung einer entspannten Referenz:

%Vor%

Wie wir sehen können, sind die obigen Beispielmethoden frei von Sperren zum Preis der Erstellung von wegwerfbaren Objekten. In jedem Fall ändert sich die Value-Eigenschaft für Konsumenten einer solchen API nicht.

Ausgleich von Kompromissen

Lock Freedom hat seinen Preis und muss sorgfältig abgewogen werden. In diesem Fall ist der Preis der Lock-Freiheit, dass Sie Instanzen von Objekten erstellen müssen, die Sie nicht verwenden werden. Dies kann ein akzeptabler Preis sein, den Sie zahlen müssen, da Sie wissen, dass das Risiko von Deadlocks und Threadkonflikten geringer ist.

In dieser bestimmten -Instanz ist die Semantik eines Singletons jedoch im Wesentlichen die Erstellung einer einzelnen Instanz eines Objekts, also würde ich mich lieber für Lazy<T> , wie @Centro in seiner Antwort zitiert hat.

Dennoch stellt sich immer noch die Frage, wann sollten wir Interlocked.CompareExchange verwenden? Ich mag dein Beispiel, es ist ziemlich zum Nachdenken anregend und viele Leute sind sehr schnell, es als falsch zu dissen, wenn es nicht schrecklich falsch ist, wie @Blindy zitiert.

Es läuft alles darauf hinaus, ob Sie die Kompromisse berechnet haben und entschieden:

  • Wie wichtig ist es, dass Sie eine einzige Instanz produzieren?
  • Wie wichtig ist es, frei von Schlössern zu sein?

Solange Sie sich der Zielkonflikte bewusst sind und eine bewusste Entscheidung treffen, neue Objekte zu erstellen, damit Sie frei von Sperren sind, könnte Ihr Beispiel auch eine akzeptable Antwort sein.

    
Anastasiosyal 16.02.2012, 17:51
quelle
9

Wenn Ihr Singleton jemals in Gefahr ist, sich mehrfach zu initialisieren, haben Sie viel schlechtere Probleme. Warum nicht einfach benutzen:

%Vor%

Absolut Thread-sicher in Bezug auf die Initialisierung.

Edit: Für den Fall, dass ich nicht klar war, ist dein Code schrecklich falsch . Sowohl der if check als auch der new sind nicht threadsicher! Sie müssen eine geeignete Singleton-Klasse verwenden.

    
Blindy 13.05.2011 19:23
quelle
6

Um "double-checked locking mambo-jambo" nicht zu verwenden oder einfach kein eigenes Singleton zu implementieren, das das Rad neu erfindet, verwenden Sie eine fertige Lösung, die in .NET 4.0 enthalten ist - Lazy<T> .

    
Centro 13.05.2011 21:05
quelle
3
%Vor%     
Oded 13.05.2011 19:23
quelle
3

Ich bin nicht überzeugt, dass Sie das vollkommen vertrauen können. Ja, Interlocked.CompareExchanger ist atomar, aber New Singleton () wird in keinem nicht-trivialen Fall atomar sein. Da es vor dem Austausch von Werten ausgewertet werden müsste, wäre dies im Allgemeinen keine Thread-sichere Implementierung.

    
Streklin 13.05.2011 19:24
quelle
3

Was ist damit?

%Vor%

Es ist die fünfte Version auf dieser Seite: Ссылка

Ich bin mir nicht sicher, aber der Autor scheint zu denken, dass es sowohl threadsicher als auch faul ist.

    
Tim 13.05.2011 19:32
quelle
2

Ihr Singleton-Initialisierer verhält sich genau so, wie er sollte. Siehe Raymond Chens Lock-free-Algorithmen: Der Singleton-Konstruktor :

  

Dies ist ein Double-Check-Lock, aber ohne die Verriegelung. Anstatt bei der ersten Konstruktion die Sperre zu übernehmen, lassen wir es einfach frei, wer das Objekt erstellen soll. Wenn fünf Threads alle diesen Code gleichzeitig erreichen, sollten wir fünf Objekte erstellen. Nachdem alle erstellt haben, was sie denken, ist das gewinnende Objekt, sie InterlockedCompareExchangePointerRelease aufgerufen, um zu versuchen, den globalen Zeiger zu aktualisieren.

     

Diese Technik ist geeignet, wenn mehrere Threads versuchen, den Singleton zu erstellen (und alle Verlierer ihre Kopie zerstören). Wenn das Erstellen des Singletons teuer ist oder unerwünschte Nebenwirkungen hat, dann möchten Sie den Free-for-All-Algorithmus nicht verwenden.

Jeder Thread erstellt das Objekt; wie es denkt, dass noch niemand es geschaffen hat. Aber während des InterlockedCompareExchange ist nur ein -Thread wirklich in der Lage, den globalen Singleton festzulegen .

Bonus lesen

  • One-Time Initialization Hilfsfunktionen ersparen Ihnen den ganzen Code selbst zu schreiben. Sie befassen sich mit allen Problemen bei der Synchronisierung und der Speicherbarriere und unterstützen sowohl das Initialisieren von Ein-Personen-Initialen als auch das Initialisieren aller Modelle.
  • Ein primitives Initialisierungsprimitiv für .NET stellt eine C # -Version desselben bereit.
Ian Boyd 31.07.2013 21:08
quelle
1

Dies ist nicht Thread-sicher.

Sie würden eine Sperre benötigen, um if() und Interlocked.CompareExchange() zusammen zu halten, und dann brauchen Sie CompareExchange nicht mehr.

    
Henk Holterman 13.05.2011 19:26
quelle
1

Sie haben immer noch das Problem, dass Sie möglicherweise Instanzen Ihres Singletons erstellen und wegwerfen. Wenn Sie Interlocked.CompareExchange() ausführen, wird der Konstruktor Singleton immer ausgeführt, unabhängig davon, ob die Zuweisung erfolgreich ist. Sie sind also nicht besser dran (oder schlechter dran, IMHO), als wenn Sie sagten:

%Vor%

Bessere Leistung bei Threadkonflikten als wenn Sie die Position von lock und den Test für null vertauscht hätten, aber auf die Gefahr hin, dass eine zusätzliche Instanz erstellt wird.

    
Nicholas Carey 13.05.2011 19:27
quelle
1

Die Auto-Eigenschaftsinitialisierung (C # 6.0) scheint nicht die Mehrfachinstanzen von Singleton zu verursachen, die Sie sehen.

%Vor%     
Clinton Chapman 18.11.2015 18:38
quelle
0

Ich denke, der einfachste Weg nach .NET 4.0 ist System.Lazy<T> :

%Vor%

Jon Skeet hat hier einen schönen Artikel hier , der viele Möglichkeiten zur Implementierung von Singleton und den Problemen jedes einzelnen behandelt eins.

    
fabriciorissetto 30.10.2015 13:30
quelle
0

Verwenden Sie keine Verriegelung. Verwenden Sie Ihre Sprachumgebung

Meist einfache Thread-sichere Implementierung ist:

%Vor%     
ZOXEXIVO 10.08.2016 20:32
quelle

Tags und Links