Busy Waiting (auch Spinning oder aktives Warten) ist ein Warteverhalten in der Programmierung: Ein Thread prüft in einer engen Schleife wiederholt, ob eine Bedingung erfüllt ist, statt zu schlafen oder zu blockieren. Bis die Bedingung eintritt, belegt der Thread dabei ununterbrochen einen Prozessorkern.

So sieht aktives Warten aus

Das einfachste Beispiel ist das Warten auf ein Flag, das ein anderer Thread setzt:

volatile int fertig = 0;
while (!fertig) {
    // warten, prüfen, warten...
}

Solange fertig null bleibt, dreht sich die Schleife, ohne dass der Thread den Prozessor freigibt. Jede CPU-Architektur braucht dafür eine atomare Prüfung beziehungsweise eine Synchronisationsvorschrift, damit die Schleife nicht nur den alten Cache-Stand liest — siehe atomare Operationen und Cache-Kohärenz.

Vorteile: Die schnellste Reaktion für sehr kurze Wartezeiten

Aktives Warten kommt ohne Betriebssystem-Aufrufe aus: kein Kontextwechsel, kein Syscall, kein Umlagern des Threads in eine Warteschlange. Wenn die Bedingung in wenigen Nanosekunden eintritt, ist Busy Waiting die latenzärmste Variante — der Thread ist sofort wieder auf dem Kern, sobald die Bedingung erfüllt ist. Genau dafür ist der Spinlock gebaut: Er sperrt kurze kritische Abschnitte, in denen ein blockierender Wechsel teurer wäre als das Warten selbst.

Kosten: CPU-Zeit, Energie und Nebenläufigkeitsprobleme

  • Ein wartender Thread verbrennt Rechenzeit, die andere Threads nutzen könnten — auf einem Ein-Kern-System ohne Umschaltung kann er das System sogar blockieren.
  • Der Stromverbrauch steigt, weil der Kern nicht in einen Sparzustand wechseln kann.
  • Bei Prioritätsverwaltung droht Prioritätsumkehr, wenn ein niederpriorer Thread den von einem hochprioren erwarteten Wert nie setzt, weil der hochpriore das CPU-Zeitscheibchen im Warten verbraucht.
  • Mehrere wartende Threads können sich wechselseitig in die Quere kommen, und ohne faire Reihenfolge kann ein einzelner Thread „verhungern".

Typische Anwendungen

  • Spinlock und andere Sperren auf Mehrkern-Systemen: Der Thread pollt die Sperrvariable.
  • Lese-Schreib-Sperren und Treiber: Warten per Polling auf ein Hardware-Register (Geräte-I/O), z. B. ob ein Datenträger bereit ist — ein klassisches Feld der Nebenläufigkeit.
  • Interrupt-Kontext im Betriebssystemkern, wo Schlafen nicht erlaubt ist: Der Linux-Kernel nutzt spin_lock() genau dort.
  • Event-Loops und Frameworks, die in jedem Durchlauf prüfen, ob neue Arbeit vorliegt.

Verfeinerungen: Pause, Backoff und Yield

Moderne CPUs kennen die x86-Instruktion PAUSE beziehungsweise cpu_relax() auf ARM: Sie signalisiert der CPU eine Spin-Schleife, optimiert Speicherzugriffe und senkt den Energieverbrauch. Bei hoher Konkurrenz hilft exponentielles Backoff (der Thread wartet nach jedem Fehlversuch etwas länger, bevor er erneut prüft) oder ein Ticket-Lock, das eine faire FIFO-Reihenfolge erzwingt. sched_yield() gibt das Zeitscheibchen frei, ohne den Thread vollständig zu blockieren — ein Mittelweg zwischen Spinnen und Schlafen.

Wann besser blockieren statt warten?

Sobald die erwartete Wartezeit merklich über ein paar hundert CPU-Zyklen hinausgeht, ist blockierendes Warten die bessere Wahl: Ein Mutex oder eine Bedingungsvariable legt den Thread schlafen; der Kernel weckt ihn erst, wenn das Ereignis eintritt. Unter Linux arbeitet dieser Mechanismus über den Futex — eine Kombination aus kurzem Spin und anschließendem Schlafen. Wichtig ist die Abgrenzung: Busy Waiting ist eine Wartestrategie mit Kosten, kein Fehlerzustand — im Gegensatz zum Livelock, bei dem Prozesse dauerhaft aktiv sind, aber keinen Fortschritt machen.

Verwandte Grundlagen: Spinlock, Mutex, Bedingungsvariable, Futex, Thread, atomare Operationen, kritischer Abschnitt.