Mike Ash Singleton: Platzieren @synchronisiert

7

Ich kam über das Mike Ash "Pflege und Fütterung von Singletons" und war ein wenig verwirrt von seinem Kommentar:

  

Dieser Code ist allerdings etwas langsam.   Ein Schloss zu nehmen ist etwas teuer.   Es ist schmerzhafter   dass die meiste Zeit,   das Schloss ist sinnlos. Das Schloss ist   nur benötigt, wenn foo null ist, was   passiert im Grunde nur einmal. Nach dem   Singleton ist initialisiert, die Notwendigkeit für   das Schloss ist weg, aber das Schloss selbst   bleibt.

%Vor%

Meine Frage ist, und es gibt keinen Zweifel, ein guter Grund dafür, aber warum können Sie nicht schreiben (siehe unten), um die Sperre zu begrenzen, wenn foo Null ist?

%Vor%

Prost Gary

    
fuzzygoat 10.03.2010, 16:53
quelle

5 Antworten

18

Denn dann unterliegt der Test einer Wettlaufsituation. Zwei verschiedene Threads können unabhängig voneinander prüfen, ob foo nil ist, und anschließend (nacheinander) separate Instanzen erstellen. Dies kann in Ihrer modifizierten Version passieren, wenn ein Thread den Test durchführt, während sich die andere noch in +[Foo alloc] oder -[Foo init] befindet, aber foo noch nicht festgelegt hat.

Übrigens würde ich es nicht so machen. Sehen Sie sich die dispatch_once() -Funktion an, mit der Sie garantieren können, dass ein Block nur einmal während der Laufzeit Ihrer App ausgeführt wird (vorausgesetzt, Sie haben GCD auf der Plattform, auf die Sie ausgerichtet sind).

    
user23743 10.03.2010, 17:01
quelle
7

Dies wird als doppelt geprüftes Sperren "Optimierung" bezeichnet. Wie überall dokumentiert, ist dies nicht sicher. Selbst wenn es nicht durch eine Compiler-Optimierung besiegt wird, wird es die Art und Weise, wie Speicher auf modernen Maschinen arbeitet, besiegen, es sei denn, Sie verwenden eine Art Zaun / Barrieren.

Mike Ash zeigt auch die richtige Lösung mit volatile und OSMemoryBarrier(); .

Das Problem ist, dass wenn ein Thread foo = [[self alloc] init]; ausführt, es keine Garantie gibt, dass, wenn ein anderer Thread foo != 0 sieht, auch alle von init ausgeführten Speicherschreibvorgänge sichtbar sind.

Siehe auch DCL und C ++ und DCL und Java für weitere Details.

    
mfazekas 11.03.2010 08:34
quelle
1

In Ihrer Version könnte die Überprüfung auf !foo gleichzeitig in mehreren Threads erfolgen, sodass zwei Threads in den Block alloc springen können und einer darauf wartet, dass der andere beendet wird, bevor er eine weitere Instanz zuweist.

>     
jessecurry 10.03.2010 17:04
quelle
1

Sie können optimieren, indem Sie nur die Sperre übernehmen, wenn foo == nil, aber danach müssen Sie erneut (innerhalb der @ synchronisierten) testen, um sich vor Wettlaufbedingungen zu schützen.

%Vor%     
David Gelhar 10.03.2010 17:04
quelle
1

Am besten, wenn Sie einen großen zentralen Versand haben

%Vor%     
Jean-alexandre Iragne 07.03.2014 11:21
quelle