O(1) hilft nicht an und für sich. In einer gesperrten Datenstruktur muss es eine einzelne atomare Instanz geben, wenn sich Ihre Datenstruktur zu ändern scheint. Alle Repräsentationsinvarianten müssen sowohl unmittelbar vor als auch unmittelbar nach diesem atomaren Moment in Kraft sein.
Wenn Sie also eine Änderung an der Datenstruktur vornehmen, besteht das wichtige Merkmal darin, dass Sie alle Änderungen an einer privaten Datenstruktur vornehmen und die Änderungen dann in einer einzelnen atomaren Anweisung austauschen können.
Die Freiheit von Sperren ist normalerweise am einfachsten, wenn Ihre Datenstrukturen unveränderlich ( rein funktional ) sind. Sie behalten einfach einen globalen Zeiger auf die aktuelle Version der Datenstruktur. Leser müssen nichts sperren. Änderungen an der Datenstruktur werden bewirkt, indem der globale Zeiger auf eine unveränderbare Datenstruktur auf eine andere ausgetauscht wird.
Zum Beispiel: Wenn Sie einen rein funktionalen Baum mit ausgeglichenem Baum haben, haben Sie:
- Zeichnen Sie den aktuellen globalen Zeiger auf die Wurzel des Baums auf.
- Erstellen Sie einen neuen Baum, der einen Knoten einfügt oder löscht. (Dies ist zeitlich und räumlich logarithmisch in Bezug auf die Anzahl der aktuell in der Struktur vorhandenen Knoten und umfasst das Erstellen neuer Knoten vom Änderungspunkt bis zur Wurzel und das Zeigen aller neuen Knoten auf die alten Teile der vorherigen Version der Datenstruktur. )
- Vergleichen Sie den globalen Zeiger atomar und tauschen Sie ihn gegen den Stamm aus. (Beachten Sie, dass dies möglicherweise fehlschlägt, wenn zwischen der Aufzeichnung des alten Stammzeigers und jetzt eine andere Änderung stattgefunden hat. In diesem Fall kehren Sie zu Schritt 1 zurück und versuchen es erneut. Dies ist die sogenannte "optimistische Parallelitätskontrolle".)
Beachten Sie, dass der wichtigste Teil das ist, was ich oben über die Aufrechterhaltung von Repräsentationsinvarianten gesagt habe. Es ist normalerweise nicht ausreichend, einen Algorithmus zu haben, der eine atomare Änderung in der Mitte des Baums vornimmt. Warum? Beispiel: Möglicherweise haben Sie einen Reader-Thread, der gerade eine Vorbestellung für den Baum durchläuft. Wenn Sie einen Knoten ändern, der ein Vorfahr des Knotens ist, den sie gerade lesen, werden Sie die Voraussetzungen ungültig machen, die sie für erzwungen hielten. Der Leser muss in der Lage sein, mit der Datenstruktur genau so zu arbeiten, wie sie vor Ihrer Änderung war, oder genau so, wie sie nach der Änderung sein wird. Nichts dazwischen.
Edit : Wie @Raphael hervorhob, gibt es Techniken, um veränderbare Datenstrukturen frei von Sperren zu machen. Ein konstruktiver Beweis dafür, dass dies immer möglich ist, ist: Solange Sie einen einzigen globalen Zeiger auf die "Spitze" Ihrer Datenstruktur haben, können Sie, auch wenn diese veränderlich ist, immer die gesamte Datenstruktur kopieren und Ihre Mods in die kopieren Kopieren Sie die Daten, und versuchen Sie dann mithilfe der optimistischen Parallelitätssteuerung, den Zeiger auf Ihre neu erstellte Datenstruktur für das Original zu vergleichen und auszutauschen. Das Schöne an funktionalen baumbasierten Datenstrukturen ist, dass sie die Kosten für das Kopieren bei einer Datenstruktur der Größe .O ( N )O(log(N))O(N)