Spinlock vs. Mutex: die wichtigsten Unterschiede

Spinlock und Mutex sind beides Sperren, die jeweils nur einen Thread gleichzeitig in einen kritischen Abschnitt lassen, unterscheiden sich aber darin, wie auf die Freigabe der Sperre gewartet wird. Ein Spinlock wartet aktiv (Busy-Waiting), indem er die Sperrvariable in einer Schleife ständig abfragt. Er belegt also auch während des Wartens die CPU, kann die Sperre aber sofort übernehmen, sobald sie frei wird. Ein Mutex legt den wartenden Thread schlafen und gibt die CPU ab; wird die Sperre freigegeben, weckt ihn das Betriebssystem. Bei sehr kurzer Haltedauer oder in Interrupt-Handlern, die nicht schlafen dürfen, verwendet man Spinlocks; bei langer Haltedauer oder wenn während des Haltens geschlafen werden könnte, Mutexe.

MerkmalSpinlockMutex
WarteverfahrenFragt die Variable in einer Schleife ab, bis die Sperre frei ist (Busy-Waiting)Ist die Sperre belegt, schläft der Thread und wird bei der Freigabe geweckt
CPU während des WartensWird weiter belegtWird abgegeben. In dieser Zeit können andere Threads die CPU nutzen
Aufwand des WartensKein Kontextwechsel; die Sperre kann sofort übernommen werden, sobald sie frei istBeim Einschlafen und Aufwachen fallen Systemaufrufe und Kontextwechsel an
Geeignete HaltedauerSehr kurz (kürzer als die Dauer eines Kontextwechsels)Lang. Wenn während des Haltens geschlafen werden könnte, etwa beim Warten auf I/O
Schlafen während des HaltensVerboten. Die wartende Seite würde die CPU weiter belegenMöglich
Bei nur einem KernWährend des Wartens kann der Thread, der die Sperre hält, nicht laufen; das Spinnen gibt sie nie frei. Im Kernel wird er mit abgeschalteter Präemption verwendetProblemlos verwendbar
Einsatz im KernelAuch in Situationen nutzbar, in denen nicht geschlafen werden darf, etwa in Interrupt-Handlern (Interrupt-Kontext). Werden Daten mit einem Interrupt geteilt, werden vor dem Sperren die Interrupts gesperrtDa er schlafen kann, nur im Prozesskontext verwendbar, nicht in Interrupt-Handlern
PrioritätsinversionWird der Thread, der die Sperre hält, angehalten, spinnt die wartende Seite weiter. Im Kernel wird dies durch Abschalten der Präemption während des Haltens verhindertAbhilfe durch Mutexe mit Prioritätsvererbung
ImplementierungsbeispielDie Variable wird mit atomaren Befehlen (Test-and-Set oder Compare-and-Swap) geändert. Test-and-Test-and-Set wartet normalerweise mit gewöhnlichen Lesezugriffen und verringert so die Kommunikation zwischen den KernenBei futex unter Linux wird ohne Konkurrenz allein mit atomaren Befehlen im Userspace gesperrt; nur bei Konkurrenz legt der Kernel den Thread schlafen. Manche Implementierungen spinnen erst kurz und schlafen dann

Die Zahlenwerte sind typische Größenordnungen und variieren je nach Prozessgeneration und Produkt.

Wann welche Variante?

Spinlock
Auf Mehrkernsystemen, wenn die Sperre nur sehr kurz gehalten wird (kürzer als der Aufwand eines Kontextwechsels); bei Verarbeitung im Kernel, bei der Schlafen nicht erlaubt ist, etwa in Interrupt-Handlern.
Mutex
Wenn die Sperre lange gehalten wird oder während des Haltens durch I/O, Speicherallokation u. Ä. geschlafen werden könnte; für den gewöhnlichen wechselseitigen Ausschluss in Userspace-Anwendungen.
Kombination
Viele Mutex-Implementierungen kombinieren die Vorteile beider: Ohne Konkurrenz wird die Sperre ohne Schlafen allein mit atomaren Befehlen erworben, und gelingt dies nicht, wird erst kurz gespinnt und dann geschlafen.

Häufige Fragen

Warum verwendet man Spinlocks, obwohl sie CPU-Zeit verschwenden?

Weil bei sehr kurzer Haltedauer kurzes Spinnen schneller ist als der Kontextwechsel für Einschlafen und Aufwachen. Außerdem sind in Situationen, in denen Schlafen nicht erlaubt ist, etwa in Interrupt-Handlern des Kernels, nur Spinlocks verwendbar.

Warum kann man in Interrupt-Handlern keinen Mutex verwenden?

Ein Mutex legt den wartenden Thread schlafen, wenn er die Sperre nicht erhält. Ein Interrupt-Handler läuft jedoch nicht als bestimmter Thread und kann daher nicht schlafen und später geweckt werden. Werden Daten mit einem Interrupt-Handler geteilt, kombiniert man einen Spinlock mit dem Sperren der Interrupts.

Was ist futex?

Ein Mechanismus unter Linux, um Mutexe u. Ä. schnell zu implementieren. Ohne Konkurrenz genügt es, die Sperrvariable im Userspace mit atomaren Befehlen zu ändern; nur wenn bei Konkurrenz gewartet werden muss, legt der Kernel den Thread per Systemaufruf schlafen. Da die meisten Sperren nicht umkämpft sind, entfällt so der Aufwand für Systemaufrufe.

Was passiert, wenn man auf einem System mit nur einem Kern einen Spinlock verwendet?

Auch wenn im Userspace gespinnt wird, kann der Thread, der die Sperre hält, auf diesem Kern nicht laufen; die Sperre wird daher nicht frei, bis die eigene Zeitscheibe aufgebraucht ist. Im Kernel von Systemen mit nur einem Kern wird der wechselseitige Ausschluss deshalb statt mit Spinlocks durch Abschalten von Präemption oder Interrupts erreicht.

Verständnis prüfen

Welche Aussage zu Spinlocks ist richtig?

  1. AEine Sperre, bei der der Thread schläft und vom Betriebssystem geweckt wird, bis die Sperre frei ist
  2. BEine Sperre, bei der in einer Schleife die Sperrvariable immer wieder geprüft wird, bis die Sperre frei ist
  3. CEin Mechanismus, der Daten schützt, indem die Festplatte rotiert
Antwort und Erläuterung anzeigen

Antwort: B. Eine Sperre, bei der in einer Schleife die Sperrvariable immer wieder geprüft wird, bis die Sperre frei ist

Ein Spinlock versucht, die Sperre mit einem atomaren Befehl zu erhalten, und wiederholt bei Misserfolg sofort den Versuch. Da der Aufwand entfällt, das Betriebssystem aufzurufen und zu schlafen bzw. aufzuwachen, arbeitet er schnell, wenn die Sperre nur sehr kurz gehalten wird. Er wird häufig im Betriebssystemkern und für den gegenseitigen Ausschluss mit Interrupt-Handlern verwendet. Nachteil ist, dass auch beim Warten weiter CPU-Zeit verbraucht wird.

In welcher Situation eignet sich ein Spinlock am wenigsten?

  1. AWenn die Sperre lange gehalten wird und wartende Threads unnötig CPU-Zeit verbrauchen
  2. BWenn die Sperre nur sehr kurz, einige zehn Nanosekunden, gehalten wird
  3. CBei Verarbeitung im Betriebssystemkern, bei der nicht geschlafen werden kann
Antwort und Erläuterung anzeigen

Antwort: A. Wenn die Sperre lange gehalten wird und wartende Threads unnötig CPU-Zeit verbrauchen

Ein auf einen Spinlock wartender Thread verbraucht CPU-Zeit, bis die Sperre frei ist. Wird die Sperre lange gehalten, ist die gesamte CPU-Zeit verschwendet und die Ausführung anderer Threads wird behindert. Gibt es nur einen einzigen Kern, wird die Sperre erst frei, wenn der sperrhaltende Thread ausgeführt wird, sodass das Warten bis zum Ende der Zeitscheibe andauert. Für längeres Warten wird ein Mutex verwendet, der schlafend wartet, oder ein Verfahren, das nur kurz kreist und dann schläft.

Warum wartet man bei Spinlocks (Test-and-Test-and-Set) normalerweise mit gewöhnlichem Lesen auf die Freigabe, statt die Sperrvariable sofort mit einem atomaren Befehl zu ändern, und verwendet den atomaren Befehl nur, wenn die Sperre frei erscheint?

  1. AWeil gewöhnliches Lesen genauer als atomare Befehle ist
  2. BWeil ein atomarer Befehl nur einmal pro Programm verwendet werden kann
  3. CWeil bei bloßem Lesen im eigenen Cache gewartet werden kann und so die Kommunikation, bei der die Cache-Zeile zwischen den Kernen hin- und herwandert, stark verringert wird
Antwort und Erläuterung anzeigen

Antwort: C. Weil bei bloßem Lesen im eigenen Cache gewartet werden kann und so die Kommunikation, bei der die Cache-Zeile zwischen den Kernen hin- und herwandert, stark verringert wird

Ein atomarer Befehl beinhaltet einen Schreibvorgang; führt ein wartender Thread ihn wiederholt aus, streiten die Kerne jedes Mal im M-Zustand um die Zeile und machen fortwährend die Kopien anderer Kerne ungültig. Bei gewöhnlichem Lesen liest jeder Kern dagegen nur weiter seine Kopie im S-Zustand im eigenen Cache, und es entsteht keine Kommunikation. Nur wenn die Sperre freigegeben wird und sich der Wert ändert, wird mit dem atomaren Befehl zugegriffen.

  1. Welche Aussage im Vergleich von Mutex (schlafend wartende Sperre) und Spinlock ist richtig?
  2. Welche Aussage zur Prioritätsumkehr (Priority Inversion) bei einem RTOS ist korrekt?
  3. Welche Aussage zur „Prioritätsvererbung“, die Prioritätsumkehr verhindern soll, ist korrekt?

Übungsfragen zu Multicore und Synchronisation

Wichtige Begriffe erklärt

Spinlock
Sperre, bei der auf das Freiwerden in einer Schleife durch ständiges Prüfen der Sperrvariable gewartet wird. Schnell bei kurzer Wartezeit, belegt aber auch während des Wartens die CPU.
Mutex
Sperre, die zu jedem Zeitpunkt nur ein Thread halten kann. Üblich ist eine Implementierung, bei der ein Thread, der sie nicht erhält, schlafen gelegt wird und die CPU abgibt; bei Freigabe weckt ihn das OS.
Kontextwechsel
Umschalten des auf der CPU ausgeführten Prozesses oder Threads. Zustände wie Register und Programmzähler werden gesichert und wiederhergestellt; beim Prozesswechsel wird auch der Adressraum umgeschaltet.
Atomarer Befehl
Befehl, der Lesen und Schreiben im Speicher als eine einzige Operation ausführt, die von anderen Kernen nicht unterbrochen werden kann. Beispiele sind CAS, Fetch-and-Add und LR/SC.
Prioritätsinversion
Phänomen, bei dem ein Task hoher Priorität auf eine von einem Task niedriger Priorität gehaltene Ressource wartet, während ein Task mittlerer Priorität den niedrigen verdrängt und weiterläuft, sodass der hohe lange warten muss.

Verwandte Vergleiche