Zweimal kontrollierte Blockierung

In der Softwaretechnik ist zweimal kontrollierte Blockierung (auch bekannt als "zweimal kontrollierte sich schließen lassende Optimierung") ein Softwaredesignmuster, das verwendet ist, um die Gemeinkosten zu reduzieren, ein Schloss durch die erste Prüfung des sich schließen lassenden Kriteriums (der "Schloss-Hinweis") zu erwerben, ohne wirklich das Schloss zu erwerben. Nur wenn die sich schließen lassende Kriterium-Kontrolle anzeigt, dass Blockierung erforderlich ist, tut die wirkliche sich schließen lassende Logik gehen weiter.

Das Muster, wenn durchgeführt, in einigen Kombinationen der Sprache/Hardware, kann unsicher sein. Zuweilen kann es als ein Antimuster betrachtet werden.

Es wird normalerweise verwendet, um Blockierung oben zu reduzieren, wenn man "faule Initialisierung" in einer Mehrgewindeumgebung, besonders wenn ein Teil des Musters von Singleton durchführt. Faule Initialisierung vermeidet, einen Wert bis zum ersten Mal zu initialisieren, wenn darauf zugegriffen wird.

Gebrauch in Java

Denken Sie zum Beispiel, dieses Codesegment auf der javanischen Programmiersprache, wie gegeben, durch http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html (sowie alle anderen javanischen Codesegmente):

//Einzelne Gewindeversion

Klasse Foo {\

privater Helfer-Helfer = ungültig;

öffentlicher Helfer getHelper {\

wenn (Helfer == ungültig) {\

Helfer = neuer Helfer ;

}\

geben Sie Helfer zurück;

}\

//andere Funktionen und Mitglieder...

}\</Quelle>

Das Problem besteht darin, dass das nicht arbeitet, wenn es vielfache Fäden verwendet. Ein Schloss muss erhalten werden, im Falle dass zwei Fäden gleichzeitig rufen. Sonst können entweder sie beide versuchen, den Gegenstand zur gleichen Zeit zu schaffen, oder man kann abwickeln, eine Verweisung auf einen unvollständig initialisierten Gegenstand zu bekommen.

Das Schloss wird durch das teure Synchronisieren erhalten, wie im folgenden Beispiel gezeigt wird.

//Richtig, aber vielleicht teure Mehrgewindeversion

Klasse Foo {\ privater Helfer-Helfer = ungültig;

Publikum hat Helfer getHelper {\synchronisiert

wenn (Helfer == ungültig) {\ Helfer = neuer Helfer ; }\ geben Sie Helfer zurück; }\ //andere Funktionen und Mitglieder...}\</Quelle>

Jedoch wird der erste Anruf den Gegenstand und nur die wenigen Fäden schaffen, die versuchen, darauf während dieses Zeitbedarfs zuzugreifen, synchronisiert zu werden; danach bekommen alle Anrufe gerade eine Verweisung auf die Mitglied-Variable.

Seit dem Synchronisieren einer Methode kann Leistung durch einen Faktor 100 oder höher, die Gemeinkosten des Erwerbens und der Ausgabe eines Schlosses jedes Mal vermindern, wenn diese Methode genannt wird, scheint unnötig: Sobald die Initialisierung vollendet worden ist, würden das Erwerben und das Veröffentlichen der Schlösser unnötig scheinen. Viele Programmierer haben versucht, diese Situation auf die folgende Weise zu optimieren:

  1. Überprüfen Sie, dass die Variable initialisiert wird (ohne das Schloss zu erhalten). Wenn es initialisiert wird, geben Sie es sofort zurück.
  2. Erhalten Sie das Schloss.
  3. Doppelkontrolle, ob die Variable bereits initialisiert worden ist: Wenn ein anderer Faden das Schloss zuerst erworben hat, kann es bereits die Initialisierung getan haben. Wenn so, geben Sie die initialisierte Variable zurück.
  4. Initialisieren Sie sonst und geben Sie die Variable zurück.

//Gebrochen hat Version mehreingefädelt

//"Zweimal kontrolliertes sich Schließen lassendes" Idiom

Klasse Foo {\ privater Helfer-Helfer = ungültig; öffentlicher Helfer getHelper {\ wenn (Helfer == ungültig) {\

synchronisiert (dieser) {\

wenn (Helfer == ungültig) {\

Helfer = neuer Helfer ;

}\

}\ }\ geben Sie Helfer zurück; }\ //andere Funktionen und Mitglieder...}\</Quelle>

Intuitiv ist dieser Algorithmus einer effizienten Lösung des Problems ähnlich. Jedoch hat diese Technik viele feine Probleme und sollte gewöhnlich vermieden werden. Denken Sie zum Beispiel die folgende Folge von Ereignissen:

  1. Fädeln Sie Benachrichtigungen ein, dass der Wert nicht initialisiert wird, so erhält er das Schloss und beginnt, den Wert zu initialisieren.
  2. Wegen der Semantik von einigen Programmiersprachen wird dem durch den Bearbeiter erzeugten Code erlaubt, die geteilte Variable zu aktualisieren, um zu einem teilweise gebauten Gegenstand hinzuweisen, bevor A beendet hat, die Initialisierung durchzuführen. Zum Beispiel in Java, wenn ein Anruf zu einem Konstrukteur inlined dann gewesen ist, kann die geteilte Variable sofort aktualisiert werden, sobald die Lagerung zugeteilt worden ist, aber bevor der inlined Konstrukteur den Gegenstand initialisiert.
  3. Fädeln Sie B-Benachrichtigungen ein, dass die geteilte Variable initialisiert worden ist (oder so erscheint es), und seinen Wert zurückgibt. Weil Faden B glaubt, dass der Wert bereits initialisiert wird, erwirbt es das Schloss nicht. Wenn B den Gegenstand verwendet, bevor die ganze durch A getane Initialisierung durch B gesehen wird (entweder weil A nicht beendet hat, es zu initialisieren, oder weil einige der initialisierten Werte im Gegenstand zum Gedächtnis B Gebrauch (Kohärenz des geheimen Lagers)) noch nicht gefiltert haben, wird das Programm wahrscheinlich abstürzen.

Eine der Gefahren, zweimal kontrolliert zu verwenden, sich in J2SE 1.4 (und frühere Versionen) schließen lassend, ist, dass es häufig scheinen wird zu arbeiten: Es ist nicht leicht, zwischen einer richtigen Durchführung der Technik und derjenigen zu unterscheiden, die feine Probleme hat. Abhängig vom Bearbeiter, dem Durchschießen von Fäden durch den Planer und die Natur anderer gleichzeitiger Systemtätigkeit, können Misserfolge, die sich aus einer falschen Durchführung der zweimal kontrollierten Blockierung ergeben, nur periodisch auftretend vorkommen. Das Reproduzieren der Misserfolge kann schwierig sein.

Bezüglich J2SE 5.0 ist dieses Problem befestigt worden. Das flüchtige Schlüsselwort stellt jetzt sicher, dass vielfache Fäden das Singleton-Beispiel richtig behandeln. Dieses neue Idiom wird in http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html: beschrieben

//Arbeiten damit erwerben Semantik für flüchtigen/veröffentlichen

//Gebrochen unter Java 1.4 und frühere Semantik für flüchtigen

Klasse Foo {\

privater flüchtiger Helfer-Helfer = ungültig;

öffentlicher Helfer getHelper {\

Helfer-Ergebnis = Helfer;

wenn (resultieren == ungültig), {\

synchronisiert (dieser) {\

resultieren Sie = Helfer;

wenn (resultieren == ungültig), {\

Helfer = resultiert = neuer Helfer ;

}\ }\ }\

geben Sie Ergebnis zurück;

}\ //andere Funktionen und Mitglieder...}\</Quelle>

Bemerken Sie den Gebrauch der lokalen Variable, die unnötig scheint. Für einige Versionen Javas VM wird es den Code um 25 % schneller und für andere machen, es wird nicht schmerzen.

Wenn der Helfer-Gegenstand statisch ist (ein pro Klassenlader), ist eine Alternative die Initialisierung auf Verlangen Halter-Idiom Sieht Auflistung 16.6 auf

//Korrigieren Sie faule Initialisierung in Java

@ThreadSafe

Klasse Foo {\

private statische Klasse HelperHolder {\

öffentlicher statischer Helfer-Helfer = neuer Helfer ;

}\

öffentlicher statischer Helfer getHelper {\

geben Sie HelperHolder.helper zurück;

}\}\</Quelle>

Das verlässt sich auf die Tatsache, dass innere Klassen nicht geladen werden, bis in ihnen Verweise angebracht wird.

Die Semantik des Feldes in Java 5 kann verwendet werden, um den Helfer-Gegenstand ohne das Verwenden sicher zu veröffentlichen:

öffentliche Klasse FinalWrapper

öffentlicher EndT-Wert;

öffentlicher FinalWrapper (T Wert) {

this.value = Wert;

}\}\

öffentliche Klasse Foo {\

privater FinalWrapper

öffentlicher Helfer getHelper {\

FinalWrapper

wenn (Streifband == ungültig) {\

synchronisiert (dieser) {\

wenn (helperWrapper == ungültig) {\

helperWrapper = neuer FinalWrapper

}\

Streifband = helperWrapper;

}\

}\

geben Sie wrapper.value zurück;

}\}\</Quelle>

Die lokale Variable ist für die Genauigkeit erforderlich. Die Leistung dieser Durchführung ist nicht notwendigerweise besser als die Durchführung.

Gebrauch in Microsoft Visual C ++

Zweimal kontrollierte Blockierung kann in Visuellem C ++ 2005 und oben durchgeführt werden, wenn der Zeigestock zur Quelle mit dem C ++ flüchtiges Schlüsselwort erklärt wird. Visueller C ++ lesen 2005 Garantien, dass sich flüchtige Variablen als Zaun-Instruktionen, als in J2SE 5.0 benehmen werden, sowohl Bearbeiter als auch Zentraleinheitseinordnung dessen verhindernd, und schreiben damit erwerben Semantik (dafür liest), und Ausgabe-Semantik (dafür schreibt). Es gibt keine solche Garantie in vorherigen Versionen von Visuellem C ++. Jedoch kann die Markierung des Zeigestocks zur Quelle als flüchtig Leistung anderswohin schaden, wenn die Zeigestock-Behauptung anderswohin im Code sichtbar ist, indem sie den Bearbeiter gezwungen wird, es als ein Zaun anderswohin zu behandeln, selbst wenn es nicht notwendig ist.

Gebrauch in Microsoft.NET (visuell grundlegend, C#)

Zweimal kontrollierte Blockierung kann effizient in.NET mit dem sorgfältigen Gebrauch der Instruktion von MemoryBarrier durchgeführt werden:

öffentliche Klasse MySingleton {\

privater statischer Gegenstand myLock = neuer Gegenstand ;

privater statischer MySingleton mySingleton = ungültig;

privat statisch bool bereit = falsch;

privater MySingleton {

}\

öffentlicher statischer MySingleton GetInstance {\

wenn (! bereit) {//1. Kontrolle

Schloss (myLock) {\

wenn (! bereit) {//2. (doppelte) Kontrolle

mySingleton = neuer MySingleton ;

System. Das Einfädeln. Faden. MemoryBarrier ;//Zaun

bereit = wahr;

}\ }\ }\

geben Sie mySingleton zurück;

}\}\</Quelle>

In diesem Beispiel ist der "Schloss-Hinweis" die bereite Fahne, die sich nur ändern kann, nachdem mySingleton völlig gebaut und gebrauchsfertig wird.

Wechselweise C# kann flüchtiges Schlüsselwort verwendet werden, um Lesen/Schreiben geltend zu machen, umzäunt den ganzen Zugang von mySingleton, der viele von der der zweimal kontrollierten sich schließen lassenden Strategie innewohnenden Wirksamkeit verneinen würde.

öffentliche Klasse MySingleton {\ privater statischer Gegenstand myLock = neuer Gegenstand ;

privater statischer flüchtiger MySingleton mySingleton = ungültig;

privater MySingleton { }\ öffentlicher statischer MySingleton GetInstance {\

wenn (mySingleton == ungültig) {//überprüfen

Schloss (myLock) {\

wenn (mySingleton == ungültig) {//Doppelprüfung, flüchtig sicherstellt, dass der Wert nochmals gelesen wird

mySingleton = neuer MySingleton ; }\ }\ }\ geben Sie mySingleton zurück; }\}\</Quelle>

Siehe auch

  • Das Idiom des Tests und Tests-Und-Satzes für einen auf niedriger Stufe sich schließen lassenden Mechanismus.
  • Initialisierung auf Verlangen Halter-Idiom für einen vor dem Faden sicheren Ersatz in Java.

Außenverbindungen


Planer-Muster / Saboteur (Begriffserklärung)
Impressum & Datenschutz