Die Lese-Schreib-Sperre (englisch Read-Write Lock, kurz RW-Lock) ist eine Synchronisations-Primitive, die mehrere Threads gleichzeitig lesen, aber nur einen Thread schreiben lässt. Ein normaler Mutex schließt jeden gleichzeitigen Zugriff aus — auch mehrere Leser. Die Lese-Schreib-Sperre nutzt die Beobachtung, dass parallele Lesezugriffe auf gemeinsame Daten sich nicht gegenseitig stören, und erlaubt sie deshalb parallel.

Grundprinzip

Die Sperre kennt zwei Modi:

  • Geteilter Modus (Shared / S-Lock): Beliebig viele Leser halten die Sperre gleichzeitig. Leser sehen stets einen konsistenten Zustand, weil kein Schreiber dazwischenfunkt.
  • Exklusiver Modus (Exclusive / X-Lock): Genau ein Schreiber hält die Sperre allein. Während des Schreibens ist weder ein weiterer Schreiber noch ein Leser zugelassen.

Ein Leser blockiert also nur einen Schreiber, ein Schreiber blockiert alle Leser und alle anderen Schreiber. Das ist die klassische Leser-Schreiber-Synchronisation für Datenstrukturen mit vielen Lese- und wenigen Schreibzugriffen.

Das Leser-Schreiber-Problem

Formalisiert wurde das Problem 1971 von Courtois, Heymans und Parnas. Die zentrale Frage ist die Fairness: Wer bekommt die Sperre, wenn Leser und Schreiber gleichzeitig warten? Drei Varianten sind üblich:

  • Leser-bevorzugt: Solange ein Leser aktiv ist, dürfen weitere Leser eintreten. Ein wartender Schreiber kann dabei aushungern (Starvation), wenn dauerhaft neue Leser kommen.
  • Schreiber-bevorzugt: Sobald ein Schreiber wartet, werden neue Leser zurückgehalten. Verhindert Schreiber-Starvation, kann aber die Leser drosseln.
  • Fair (FIFO): Die Sperre wird in Ankunftsreihenfolge vergeben; niemand wird bevorzugt oder ausgehungert.

Einsatz in der Praxis

Lese-Schreib-Sperren sind überall dort sinnvoll, wo gemeinsame Daten häufig gelesen und selten geändert werden: Caches, Konfigurationsdaten, Wörterbücher, Routing-Tabellen. Gängige Implementierungen:

  • POSIX-Threads: pthread_rwlock_t mit pthread_rwlock_rdlock() / pthread_rwlock_wrlock()
  • C++17: std::shared_mutex mit std::shared_lock für Leser und std::unique_lock für Schreiber
  • Java: ReentrantReadWriteLock (Interface ReadWriteLock)
  • Go: sync.RWMutex mit RLock() / Lock()
  • Linux-Kernel: rwlock_t im Kern, rwsem (Reader-Writer-Semaphor) in Treibern; in Prozessen basiert die glibc-Variante intern auf einem Futex

In Datenbanken entspricht die Idee den S- und X-Sperren der Lock-Verwaltung: Viele Transaktionen können gleichzeitig lesen, Schreiben erfolgt exklusiv.

Fallstricke

  • Writer-Starvation: Leser-bevorzugte Implementierungen können Schreiber dauerhaft blockieren — im Zweifel eine faire oder Schreiber-bevorzugte Variante wählen.
  • Upgrade-Gefahr: Read-Lock in Write-Lock umzuwandeln (Upgrade) kann Verklemmungen erzeugen, wenn zwei Leser gleichzeitig upgraden wollen — siehe Deadlock. Der umgekehrte Weg (Downgrade von exklusiv auf geteilt) ist unkritisch.
  • Overhead: Die Sperre muss Leser zählen und verwalten; bei sehr kurzen kritischen Abschnitten ist ein einfacher Mutex oft schneller als ein RW-Lock. Messen statt raten.
  • Alternativen: Für einfache kurze Daten nutzt der Linux-Kernel seqlock (Sequenzsperre) oder Read-Copy-Update statt RW-Locks.

Verwandte Grundlagen: Mutex, Semaphor, kritischer Abschnitt, atomare Operationen, Nebenläufigkeit, Thread.