Sperrung in .NET

Ich stieß auf diesen Artikel, der besprach, warum das Double-Check-Locking-Paradigma in Java gebrochen ist. Ist das Paradigma für .NET (insbesondere C #) gültig, wenn Variablen als volatile deklariert werden?

    Das Implementieren des Singleton-Musters in C # spricht in der dritten Version von diesem Problem.

    Es sagt:

    Die Variable volatile der Instanz kann es funktionieren lassen, ebenso wie explizite Speicherbarrieren-Aufrufe, obwohl im letzteren Fall sogar Experten nicht genau übereinstimmen können, welche Barrieren erforderlich sind. Ich tendiere dazu, Situationen zu vermeiden, in denen Experten nicht zustimmen, was richtig und was falsch ist!

    Der Autor scheint zu implizieren, dass Doppelverriegelung weniger wahrscheinlich ist als andere Strategien und sollte daher nicht verwendet werden.

    Double-Checking-Locking funktioniert jetzt sowohl in Java als auch in C # (das Java-Speichermodell wurde geändert und dies ist einer der Effekte). Sie müssen es jedoch genau richtig machen. Wenn Sie die Dinge auch nur geringfügig durcheinander bringen, verlieren Sie am Ende möglicherweise die Fadensicherheit.

    Wie andere Antworten gesagt haben, gibt es viel bessere Möglichkeiten, das Singleton-Muster zu implementieren. Persönlich, wenn ich in einer Situation bin, in der ich zwischen zweifach überprüfter Sperrung und “Sperrung jedes Mal” -Kode wählen muss, würde ich jedes Mal auf Sperrung gehen, bis ich wirklich Beweise dafür bekommen hätte, dass es einen Engpass verursacht hat. Wenn es um das Einfädeln geht, ist ein einfaches und offensichtlich korrektes Muster viel wert.

    .NET 4.0 hat einen neuen Typ: ” Lazy , der alle Bedenken bezüglich der falschen Darstellung des Musters beseitigt. Es ist Teil der neuen Task Parallel Library.

    Weitere Informationen finden Sie im MSDN Parallel Computing Dev Center: http://msdn.microsoft.com/en-us/concurrency/default.aspx

    BTW, es gibt einen Backport (ich glaube, es ist nicht unterstützt) für .NET 3.5 SP1 hier verfügbar.

    Beachten Sie, dass in Java (und höchstwahrscheinlich auch in .Net) ein doppelt geprüftes Sperren für die Singleton-Initialisierung völlig überflüssig und auch nicht notwendig ist. Da classn erst bei ihrer ersten Verwendung initialisiert werden, wird die gewünschte faule Initialisierung bereits dadurch erreicht;

     private static Singleton instance = new Singleton(); 

    Wenn Ihre Singleton-class keine Konstanten enthält, auf die zugegriffen werden kann, bevor eine Singleton-Instanz zum ersten Mal verwendet wird, ist dies alles, was Sie tun müssen.

    Ich habe ein doppeltes Checking-Locking erhalten, indem ich einen booleschen Wert verwende (dh mit einem Primitiv, um die faule Initialisierung zu vermeiden):

    Der Singleton, der Boolean verwendet, funktioniert nicht. Die Reihenfolge der Vorgänge zwischen verschiedenen Threads ist nicht garantiert, es sei denn, Sie durchlaufen eine Speicherschranke. Mit anderen Worten, created = true kann, wie aus einem zweiten Thread ersichtlich, vor instance= new Singleton();

    Ich verstehe nicht, warum alle Leute sagen, dass das Double-Check-Locking ein schlechtes Muster ist, aber passen Sie den Code nicht an, damit er richtig funktioniert. Meiner Meinung nach sollte dieser unten stehende Code gut funktionieren.

    Wenn jemand mir sagen könnte, ob dieser Code unter dem Problem leidet, das in Camerons Artikel erwähnt wurde, dann tu es bitte.

     public sealed class Singleton { static Singleton instance = null; static readonly object padlock = new object(); Singleton() { } public static Singleton Instance { get { if (instance != null) { return instance; } lock (padlock) { if (instance != null) { return instance; } tempInstance = new Singleton(); // initialize the object with data instance = tempInstance; } return instance; } } } 

    Ich verstehe nicht ganz, warum es eine Reihe von Implementationsmustern auf doppelt überprüftem Locking gibt (offenbar um Compiler-Idiosynkrasien in verschiedenen Sprachen zu umgehen). Der Wikipedia-Artikel zu diesem Thema zeigt die naive Methode und die möglichen Wege zur Lösung des Problems, aber keine sind so einfach (in C #):

     public class Foo { static Foo _singleton = null; static object _singletonLock = new object(); public static Foo Singleton { get { if ( _singleton == null ) lock ( _singletonLock ) if ( _singleton == null ) { Foo foo = new Foo(); // Do possibly lengthy initialization, // but make sure the initialization // chain doesn't invoke Foo.Singleton. foo.Initialize(); // _singleton remains null until // object construction is done. _singleton = foo; } return _singleton; } } 

    In Java würden Sie synchronisierte () statt lock () verwenden, aber es ist im Grunde die gleiche Idee. Wenn es eine mögliche Inkonsistenz beim Zuweisen des Singleton-Feldes gibt, warum sollte man dann nicht zuerst eine lokal begrenzte Variable verwenden und dann das Singleton-Feld zum letzten möglichen Zeitpunkt zuweisen, bevor der kritische Abschnitt verlassen wird? Fehle ich etwas?

    Es gibt das Argument von @ michael-borgwardt, dass in C # und Java das statische Feld nur einmal beim ersten Gebrauch initialisiert wird, aber dieses Verhalten ist sprachspezifisch. Und ich habe dieses Muster häufig zur langsamen Initialisierung einer Collection-Eigenschaft verwendet (zB user.Sessions).

    Ich habe ein doppeltes Checking-Locking erhalten, indem ich einen booleschen Wert verwende (dh mit einem Primitiv, um die faule Initialisierung zu vermeiden):

     private static Singleton instance; private static boolean created; public static Singleton getInstance() { if (!created) { synchronized (Singleton.class) { if (!created) { instance = new Singleton(); created = true; } } } return instance; }