Begriff
Mutual Exclusion (Mutex)
Warum wichtig?
Dieser Begriff ist ein Knoten im SengakujiWorks-Wissensnetz. Nutze Level 0 für die erste Einordnung, Level 1 für Praxis, Level 2 für technische Struktur und Level 3 für Grenzen, Fallstricke und Expertenkontext.
Stell dir eine Flugzeugtoilette vor. Es gibt nur eine. Wenn sie "Besetzt" ist, kann niemand anders rein. Man muss draußen warten. Erst wenn derjenige rauskommt ("Frei"), kann der Nächste rein. Das ist Mutual Exclusion. Es verhindert Chaos (dass zwei Leute gleichzeitig auf der Toilette sitzen). In der IT ist die Toilette meistens eine Variable oder eine Datei, auf die zwei Programme zugreifen wollen.
Merksatz: Ein Prinzip, das verhindert, dass zwei Prozesse gleichzeitig auf einen kritischen Bereich (Shared Resource) zugreifen.
In Code sieht das so aus (Pseudocode):
mutex.lock() # Tür abschließen
konto += 100 # Kritischer Bereich (Geld ändern)
mutex.unlock() # Tür aufschließen
Wenn Thread B ankommt und lock() ruft, während Thread A noch drin ist, wird Thread B vom Betriebssystem schlafen gelegt, bis Thread A unlock() ruft.
1. Spinlock vs. Mutex
- Mutex: Wenn besetzt, schläft der Thread (Context Switch). Gut, wenn man lange warten muss.
- Spinlock: Der Thread fragt in einer Endlosschleife: "Jetzt frei? Jetzt frei? Jetzt frei?" (Busy Waiting). Verbrennt CPU, ist aber schneller, wenn man nur nanosekundenlang warten muss (kein Context Switch Overhead).
2. Reentrant Mutex
Darf derjenige, der den Schlüssel hat, nochmal abschließen?
- Normal: Nein -> Deadlock (er wartet auf sich selbst).
- Reentrant: Ja. Er muss dann aber auch zweimal aufschließen. (Wichtig für rekursive Funktionen).
1. Cache-Ping-Pong & False Sharing (Spinlock Penalty)
Wenn 24 Threads in einer High-Performance-Machine denselben Mutex-Integer ("0=Besetzt, 1=Frei") umkämpfen, zünden sie oft Spinlocks (Busy Wait).
Auf CPU-Hardwareebene entsteht dabei eine thermonukleare Leistungs-Implosion namens Cache Ping-Pong. Der L3/L2-Cache Core 1 flusht das Lock-Bit rigoros in seinen L1. Core 2 fragt per CMPXCHG (Compare and Exchange) hartnäckig am Bus nach. Das MESI (Modified, Exclusive, Shared, Invalid) Cache-Kohärenz-Protokoll fängt an, hunderte Millionen Mal pro Sekunde die Cacheline (typisch 64 Bytes) wild über den Motherboard-Bus hin und her zu schicken, um allen Cores die Ungültigmachende Illusion der Konstanz zu wahren. Die Bus-Bandbreite crasht komplett! Das gesamte Linux-OS stirbt fast, weil Cachelines "verbrennen", noch wilder betrieben bei "False Sharing", wenn die App nutzlose Array-Variablen zufällig in der gleichen 64-Byte Cacheline ablegt wie das umkämpfte Lock-Flag.
2. Futex (Fast Userspace Mutex)
Alte OS-Mutexe aus den 1990ern erforderten immer einen extrem teuren "Context Switch" (Kernel Ring-0 Eintritt via Syscall). Ein lock() dauerte gut 2000 CPU-Cycles, selbst wenn sich überhaupt kein fremder Thread darum bewarb ("Uncontended Lock").
Die C-Library Lösung (seit 2003 im Linux) ist der Futex.
Es ist eine Mischung aus RAM und Kernel-Syscall. Im "Happy Path" prüft der Userspace C++ Code das Lock-Bit einfach im User-RAM per atomarer CPU-Befehle (ca. 10 Cycles). Null Kernel Eintritt! Erst wenn die Funktion sieht "Verdammt, Flag ist 0, jemand schnarcht drinnen", fällt sie ins System zurück (sys_futex) und ruft den Scheduler an: "Leg mich schlafen und weck mich, wenn Adresse X sich ändert!". Futexe bescherten Linux radikale Skalierbarkeit für Webserver und Multi-Thread Engines.
3. Read/Write Locks (Readers-Writer Problem)
Der Standard-Mutex ist ein Hammer (1 User only).
Die Realität benötigt Skalpelle. Wenn 50 User den Kontostand nur lesen wollen, könnten sie dies sicher gigabyteweise parallel tun, solange nicht ein 51. User schreiben will. Wäre alles über einen nativen Mutex geleitet, sterben die Lesevorgänge im brutalen Warteschlangen-Timeout.
Ein RWLock (std::shared_mutex in C++) balanciert Counters: Es erlaubt Milliarden lock_shared() Pass-Durchgänge zeitgleich. Fordert Core 4 jedoch ein lock() (Schreibe-Request), springt das Lock intern in den "Writer-Waiting"-State. Es versperrt die Tür für neue Reader (!), wartet weinend bis die zirkulierenden In-Flight Reader sich entspannt aus-drainiert haben (das "Readers Drain"), stürzt als einziger Exklusiv-Writer durchs Tor und öffnet anschließend die Fluten wieder. Perfekt für R/W-Ratios von 99:1%.
Quick-Check
Was passiert, wenn ich das
unlockvergesse?Katastrophe. Die Ressource bleibt für immer gesperrt. Alle anderen Threads warten ewig (Deadlock/Starvation). In modernen Sprachen nutzt mantry...finallyoderwith-Blöcke, die automatisch unlocken, auch bei Fehlern.Unterschied zu Semaphoren?
Ein Mutex ist eine Semaphore mit Zähler 1 ("Es gibt genau 1 Toilette"). Eine Semaphore kann auch bis 10 zählen ("Es gibt 10 Parkplätze").Kostet Locking Zeit?
Ja. Viel Locking macht parallele Programme langsam ("Contention"). Das Ziel von gutem Design ist "Lock-Free Programming".