Ein Semaphor ist ein Synchronisationsmechanismus, mit dem nebenläufige Threads oder Prozesse den Zugriff auf gemeinsame Ressourcen steuern. Erfunden hat ihn Edsger W. Dijkstra 1965 für das Betriebssystem THE — bis heute ist er eines der wichtigsten Werkzeuge der Nebenläufigkeit.

Wie ein Semaphor funktioniert

Ein Semaphor besteht aus einem nichtnegativen Zähler und einer Warteschlange. Darauf laufen zwei Operationen:

  • P() (auch wait oder down genannt): Der Zähler wird um 1 verringert. Ist er bereits 0, blockiert der aufrufende Thread und wird in die Warteschlange eingereiht.
  • V() (auch signal oder up genannt): Der Zähler wird um 1 erhöht; wartet ein Thread in der Warteschlange, wird er geweckt.

Die Namen kommen aus dem Niederländischen: P steht für „proberen" (prüfen), V für „verhogen" (erhöhen). Ein binärer Semaphor pendelt nur zwischen 0 und 1 und schützt damit genau eine Ressource. Ein Zählsemaphor verwaltet einen ganzen Pool, etwa fünf freie Datenbankverbindungen oder zehn Plätze in einem Puffer: Beim Eintritt ruft ein Thread P() auf, beim Verlassen V().

Producer-Consumer als klassisches Beispiel

Produzenten legen Daten in einen gemeinsamen Puffer, Konsumenten entnehmen sie. Ein Zählsemaphor mit dem Wert der freien Pufferplätze verhindert, dass ein Produzent in einen vollen Puffer schreibt: Er muss vor dem Einfügen P() ausführen. Das Gegenstück für die belegten Plätze sorgt dafür, dass Konsumenten bei leerem Puffer warten. So entsteht weder Race Condition noch verlorenes Datenmaterial.

Der entscheidende Unterschied zum Mutex

Ein Semaphor hat keinen Eigentümer: Jeder Thread darf V() ausführen, auch wenn ein anderer Thread das P() durchgeführt hat. Beim Mutex ist das anders — dort darf nur der Thread entsperren, der gesperrt hat. Für reine Zugriffssperren nimmt man deshalb meist einen Mutex; der Semaphor glänzt beim Verteilen begrenzter Ressourcen und bei Warteschlangen.

Praxis

  • POSIX bietet sem_t mit sem_wait und sem_post, Java java.util.concurrent.Semaphore, Python threading.Semaphore.
  • Go nutzt idiomatisch Channels statt Semaphoren; ein Channel mit Kapazität N verhält sich wie ein Zählsemaphor.
  • Intern stützen sich Semaphore auf atomare Hardware-Operationen und blockieren bei langen Wartezeiten den Thread, statt zu „drehen" (busy wait).

Wie jeder Synchronisationsmechanismus braucht auch der Semaphor Disziplin: Falsch verschachtelte P()- und V()-Aufrufe können zu Deadlocks führen, in denen Threads endlos aufeinander warten. Der geschützte Bereich selbst heißt kritischer Abschnitt — dort muss der Code exklusiv laufen, egal ob mit Semaphor, Mutex oder atomaren Operationen.