Mit Mutexen haben wir nun ein Mittel zur blockierenden Synchronisation kennengelernt.
Ähnlich wie es mit den Simaphoren ist.
Im Gegensatz dazu wollen wir uns im folgenden Mal die nicht blockierende Synchronisation
anschauen.
Der wesentliche Unterschied im Einsatz von nicht blockierender und blockierender Synchronisation
ist, dass wir im blockierenden Fall ein pessimistisches Anwendungsszenario haben.
Das bedeutet, wir gehen davon aus, dass auf kritische Abschnitte, die wir schützen müssen,
häufig gleichzeitig zugegriffen wird.
Dementsprechend müssen wir gegenseitigen Ausschluss schaffen, damit dies eben nicht
vorkommt.
Im optimistischen Fall hingegen gehen wir davon aus, dass der Zugriff auf ein kritisches
Datum relativ selten geschieht.
Dementsprechend müssen wir nicht präventiv den kritischen Abschnitt schützen, sondern
können einfach beginnen, die Daten zu lesen und zu bearbeiten.
Und erst wenn wir ein Ergebnis haben, das wir zurückschreiben wollen, prüfen wir, ob
in der Zwischenzeit ein anderer Thread bereits die ursprünglichen Daten verändert hat.
Falls dies der Fall ist, verwerfen wir unser Ergebnis und wiederholen den Prozess.
Hier sind wir optimistisch und gehen davon aus, dass dies selten geschieht.
Um nicht blockierende Synchronisation zu implementieren, brauchen wir oft Unterstützung von der Hardware,
zum Beispiel in Form von bestimmten Instruktionen wie der CAS-Operation, dem Compare and Swap.
Mehr Details dazu gibt es in der Vorlesung.
Die Compare and Swap-Operation hat folgende Eigenschaften.
Sie erwartet drei Argumente.
Einmal eine Speicheradresse, auf die sie zugreifen soll und das zweite Argument ist der Wert,
von dem wir erwarten, dass es sich dahinter befindet.
Und das dritte Argument ist ein Wert, der an die Speicherstelle geschrieben werden soll.
Was nun also passiert bei der Ausführung von Compare and Swap ist, dass die Operation
in einem atomaren Schritt, das heißt gleichzeitig, prüft, ob an der gewählten Speicherstelle
genau der Wert steht, den wir erwarten und falls dies der Fall ist, wird dieser überschrieben
mit dem dritten Argument.
Außerdem können wir anhand des Rückgabewerts feststellen, ob ein Schreibzugriff nun stattgefunden
hat oder nicht.
Die Verwendung der CAS-Operation sieht nun typischerweise so aus.
Wir holen uns eine lokale Kopie einer kritischen Variable.
Anschließend können wir Berechnungen durchführen.
Und sobald wir fertig sind, können wir nun versuchen, das Ergebnis, das wir neu berechnet
haben, zurückzuschreiben.
Dafür prüfen wir einmal, ob der alte Wert, den wir uns kopiert haben, noch immer an der
entsprechenden Speicheradresse steht.
Damit können wir feststellen, ob in der Zwischenzeit ein anderer Thread darauf zugegriffen hat
oder nicht.
Falls kein anderer Thread darauf zugegriffen hat, können wir unser Ergebnis wegspeichern.
Das machen wir mit der CAS-Operation.
Nun müssen wir aber auf den Fall vorbereitet sein, dass eben in der Zwischenzeit doch ein
anderer Thread es geschafft hat, sein Wert abzuspeichern, so dass bei uns die CAS-Operation
den Rückgabewert falls hat.
In dem Fall müssen wir den gesamten Vorgang wiederholen, das bedeutet, wir holen uns
erneut eine lokale Kopie der kritischen Variable und berechnen unser Ergebnis neu.
Schließlich können wir wieder versuchen, das Ergebnis wegzuschreiben.
Und dies geht so lange, bis es uns einmal gelingt.
Zugänglich über
Offener Zugang
Dauer
00:08:04 Min
Aufnahmedatum
2020-12-14
Hochgeladen am
2020-12-14 09:30:10
Sprache
de-DE