Ich habe Herlihy und Wing in den letzten 15 Jahren viele Male gelesen. Es ist eine sehr schwierige Lektüre. Und das ist bedauerlich, denn obwohl es einige Feinheiten an den Rändern gibt, ist die Grundidee eigentlich ganz vernünftig.
Kurz gesagt: Linearisierbarkeit ist wie Serialisierbarkeit, jedoch mit der zusätzlichen Anforderung, dass die Serialisierung zusätzliche Ordnungseinschränkungen zwischen den Transaktionen berücksichtigt. Das Ziel ist es, Ihnen zu ermöglichen, rigoros über eine einzelne atomare Datenstruktur nachzudenken, anstatt auf einmal über das gesamte System nachdenken zu müssen.
Linearisierbarkeit ist auch einfach zu erreichen: Verknüpfen Sie einfach einen Mutex mit dem Objekt, das Sie linearisieren möchten. Jede Transaktion für dieses Objekt beginnt mit dem Sperren des Mutex und endet mit dem Entsperren des Mutex.
Hier sind die Definitionen, die ich verwenden werde:
Ein System ist serialisierbar, wenn eine Reihe von Transaktionen über eine Reihe von Daten hinweg ausgeführt wird, jedes Ergebnis der Ausführung der Transaktionen dasselbe ist, als ob die Transaktionen in einer bestimmten Reihenfolge ausgeführt würden, und die Vorgänge innerhalb jeder Transaktion in ihrer Transaktion in der Reihenfolge enthalten sind angegeben durch den Transaktionscode.
Serialisierbarkeit verhindert das Auftreten einer Verschachtelung von Operationen zwischen verschiedenen Transaktionen und setzt voraus, dass die gewählte Reihenfolge der Transaktionen der Kausalität entspricht (wenn Transaktion A den Wert x schreibt und Transaktion B den Wert x liest, den A geschrieben hat, muss Transaktion A Transaktion B vorangehen Die gewählte serielle Reihenfolge.) Es wird jedoch nichts über andere Einschränkungen bei der Reihenfolge von Transaktionen gesagt (insbesondere nichts über Prozesse und die Reihenfolge, in der Prozesse Ereignisse wahrnehmen.)
Es gibt eine weitere verwandte Idee, die Einschränkungen hinsichtlich der Reihenfolge hinzufügt, in der Vorgänge ausgeführt werden (wobei jedoch nicht nur einzelne Lese- / Schreibvorgänge von Transaktionen gesprochen werden):
Ein System ist sequentiell konsistent, wenn das Ergebnis einer Ausführung dasselbe ist, als ob die Operationen aller Prozesse in einer sequentiellen Reihenfolge ausgeführt würden, und die Operationen jedes einzelnen Prozesses in dieser Reihenfolge in der von seinem Programm festgelegten Reihenfolge angezeigt werden. ( Lamport, "Wie erstelle ich einen Multiprozessor-Computer, der Multiprozessor-Programme korrekt ausführt", IEEE T Comp 28: 9 (690-691), 1979 ).
Die Definition der sequentiellen Konsistenz impliziert, dass wir nur sequentielle Reihenfolgen akzeptieren, bei denen für jede Speicherstelle (Objekt) die induzierte sequentielle Reihenfolge von Operationen der Regel entspricht, dass der von jeder Leseoperation an die Stelle x
zurückgegebene Wert der gleiche Wert sein muss, von dem geschrieben wurde die unmittelbar vorhergehende Schreiboperation an die Stelle x
in der sequentiellen Reihenfolge.
Die Linearisierbarkeit hat die gute Absicht, (a) den Begriff der Transaktionen (aus der Serialisierung) mit dem Begriff zu kombinieren, dass Prozesse die von ihnen ausgegebenen Operationen in der Reihenfolge (aus der sequentiellen Konsistenz) abschließen, und (b) die Korrektheitskriterien einzuengen, um über die einzelnen zu sprechen Objekt isoliert, anstatt Sie zu zwingen, über das System als Ganzes nachzudenken. (Ich möchte sagen können, dass die Implementierung meines Objekts auch in einem System korrekt ist, in dem es andere Objekte gibt, die nicht linearisierbar sind.) Ich glaube, Herlihy und Wing haben möglicherweise versucht, einen Monitor genau zu definieren .
Teil (a) ist "einfach": Eine sequentielle konsistenzähnliche Anforderung wäre, dass die von jedem Prozess ausgegebenen Transaktionen für das Objekt in der resultierenden Reihenfolge in der vom Programm angegebenen Reihenfolge erscheinen. Eine serialisierungsähnliche Anforderung wäre, dass sich alle Transaktionen auf dem Objekt gegenseitig ausschließen (serialisiert werden können).
Die Komplexität ergibt sich aus Ziel (b) (in der Lage sein, über jedes Objekt unabhängig von allen anderen zu sprechen).
In einem System mit mehreren Objekten ist es möglich, dass Operationen für Objekt B Einschränkungen in der Reihenfolge enthalten, in der Operationen für Objekt A aufgerufen wurden. Wenn wir die gesamte Systemhistorie betrachten, sind wir auf bestimmte sequentielle Reihenfolgen und beschränkt müssen andere ablehnen. Wir wollten jedoch ein Korrektheitskriterium, das wir isoliert verwenden können (Überlegung, was mit Objekt A passiert, ohne die globale Systemgeschichte anzugreifen).
Beispiel: Angenommen, ich versuche, über die Richtigkeit von Objekt A, das eine Warteschlange ist, zu streiten. Angenommen, Objekt B ist ein Speicherort, und ich habe die folgenden Ausführungsverläufe: Thread 1: A.enqueue (x), A. dequeue () (gibt y zurück). Thread 2: A.enqueue (y), A.dequeue () (gibt x zurück). Gibt es eine Verschachtelung von Ereignissen, die es ermöglichen würde, dass diese Implementierung der Warteschlange korrekt ist? Ja:
Thread 1 Thread 2
A.enqueue(x) ...
... A.enqueue(y)
... A.dequeue() (returns x)
A.dequeue(y) (returns y) ...
Was ist nun, wenn der Verlauf ( einschließlich Objekt B ) wie folgt lautet: B beginnt mit dem Wert 0. Thread 1: A.enqueue (x), A.dequeue () (gibt y zurück), B.write (1). Thread 2: B.read () (gibt 1 zurück) A.enqueue (y), A.dequeue () (gibt x zurück).
Thread 1 Thread 2
A.enqueue(x) ...
A.dequeue() (returns y) ... (uh oh!)
B.write(1) ...
... B.read() (returns 1)
... A.enqueue(y)
... A.dequeue() (returns x)
Nun möchten wir, dass unsere Definition von "Korrektheit" besagt, dass diese Historie anzeigt, dass entweder unsere Implementierung von A fehlerhaft ist oder unsere Implementierung von B fehlerhaft ist, da es keine Serialisierung gibt, die "sinnvoll" ist (entweder muss Thread 2 lesen) Ein Wert von B, der noch nicht geschrieben wurde, oder Thread 1 muss einen Wert von A, der noch nicht in die Warteschlange gestellt wurde, aus der Warteschlange entfernen erlaubt eine Geschichte wie die zweite, dann ist sie eindeutig falsch.
Die durch die Linearisierung hinzugefügten Einschränkungen sind also durchaus vernünftig (und sogar für einfache Datenstrukturen wie FIFO - Warteschlangen erforderlich). Sie lauten wie folgt: "Ihre Implementierung sollte dequeue () einen Wert nicht zulassen, der erst in der Warteschlange () gespeichert wird Zukunft." Linearisierbarkeit ist recht einfach (und natürlich) zu erreichen: Ordnen Sie Ihrem Objekt einfach einen Mutex zu, und jede Transaktion beginnt mit dem Sperren und endet mit dem Entsperren. Das Denken über die Linearisierbarkeit wird schwierig, wenn Sie versuchen, Ihre Atomarität mit nicht blockierenden oder sperrenden oder wartefreien Techniken anstelle einfacher Mutexe zu implementieren.
Wenn Sie an einigen Hinweisen auf die Literatur interessiert sind, habe ich Folgendes gefunden (obwohl ich denke, dass die Diskussion über "Echtzeit" eine der Red Herings ist, die die Linearisierbarkeit schwieriger machen, als sie sein muss.) Https: // stackoverflow.com/questions/4179587/differenz zwischen-linearisierbarkeit und-serialisierbarkeit