- Prozesse, Threads und Event Loops unterscheiden
- verstehen, warum Warten und Rechnen unterschiedlich behandelt werden müssen
- die Stärken und Grenzen von Node.js, Python und anderen Laufzeiten einordnen
- Wettlaufsituationen erkennen und mit Sperren oder atomaren Operationen verhindern
Ein Webserver bearbeitet selten nur eine Anfrage zur Zeit. Während ein Nutzer seine Kurse lädt, bucht ein anderer einen Termin und ein dritter lädt eine Datei hoch. Wie ein Programm viele Dinge gleichzeitig erledigt, bestimmt maßgeblich, wie viele Nutzer ein Server bedienen kann, wie schnell er antwortet und welche Fehler auftreten können. Dieses Thema heißt Nebenläufigkeit. Es klingt nach Betriebssystem-Theorie, hat aber sehr praktische Folgen für jeden Systementwurf – von der Wahl der Programmiersprache bis zur Frage, warum ein Kurs plötzlich überbucht ist.
1. Prozesse, Threads, Event Loop
| Modell | Funktionsweise | Typisch für |
|---|---|---|
| Mehrere Prozesse | voneinander getrennte Programme mit eigenem Speicher | Python mit Gunicorn/Uvicorn-Workern, PHP-FPM |
| Mehrere Threads | parallele Ausführungsstränge in einem Prozess, gemeinsamer Speicher | Java, Go (Goroutinen), C# |
| Event Loop | ein Thread, der zwischen Aufgaben wechselt, sobald eine wartet | Node.js, Python asyncio, Browser |
Der entscheidende Unterschied liegt in der Art der Arbeit. Die meisten Webanwendungen verbringen den größten Teil ihrer Zeit mit Warten: auf die Datenbank, auf eine externe Schnittstelle, auf das Dateisystem, auf ein KI-Modell. Diese Wartezeiten nennt man I/O-gebunden. Andere Aufgaben erfordern echtes Rechnen: Bilder verkleinern, PDFs erzeugen, große Datenmengen auswerten. Sie sind CPU-gebunden. Je nach Art der Arbeit eignet sich ein anderes Modell.
2. Warten und Rechnen
Die Simulation zeigt eine Anfrage, die vier Aufgaben erledigen muss: drei, die vor allem warten, und eine, die rechnet. Vergleichen Sie die Modelle:
≈ 315 ms
Ein Event Loop ist hervorragend für Wartezeiten. Während die Datenbank antwortet, kann derselbe Thread schon die nächste Anfrage bearbeiten. Deshalb kann ein einziger Node.js-Prozess Tausende gleichzeitige Verbindungen halten, solange er vor allem wartet. Die Schwäche zeigt sich bei Rechenarbeit: Solange eine rechenintensive Aufgabe läuft, blockiert sie den einzigen Thread, und alle anderen Anfragen müssen warten. Ein einziger schlecht programmierter Endpunkt, der eine große Datei synchron verarbeitet, kann so einen ganzen Server lahmlegen.
3. Laufzeiten im Vergleich
Node.js basiert auf einem Event Loop und ist ideal für Webanwendungen und Schnittstellen mit vielen gleichzeitigen, überwiegend wartenden Anfragen. Für Rechenarbeit gibt es Worker Threads, in der Praxis werden solche Aufgaben aber meist ausgelagert. Python hat mit dem Global Interpreter Lock eine Besonderheit: In der Standardimplementierung führt immer nur ein Thread gleichzeitig Python-Code aus. Für Wartezeiten eignen sich asyncio und Frameworks wie FastAPI hervorragend, für Rechenarbeit mehrere Prozesse oder Bibliotheken, die intern in C rechnen. Neuere Python-Versionen experimentieren mit einer Variante ohne dieses Lock, die aber noch nicht Standard ist. Go und Java nutzen leichtgewichtige Threads und können Warten und Rechnen gleichermaßen gut parallelisieren.
Für den Systementwurf folgt daraus: Die Laufzeit prägt die Architektur. Eine Node.js- oder FastAPI-Anwendung wird typischerweise mit mehreren Prozessen pro Server betrieben, um alle Prozessorkerne zu nutzen, und schwere Arbeit wird in Hintergrund-Worker ausgelagert. Die Zahl der Prozesse, die Größe der Verbindungspools zur Datenbank und die Zeitlimits müssen zueinander passen – sonst warten Anfragen auf freie Verbindungen, obwohl der Server kaum ausgelastet ist.
4. Wettlaufsituationen
Nebenläufigkeit bringt eine Fehlerklasse mit sich, die besonders tückisch ist, weil sie nur selten und unter Last auftritt. Spielen Sie das klassische Beispiel durch: Zwei Menschen buchen gleichzeitig den letzten Platz in einem Yogakurs.
- 1A liest: 1 freier Platz
- 2B liest: 1 freier Platz
- 3A schreibt: 0 frei, Buchung bestätigt
- 4B schreibt: 0 frei, Buchung bestätigt – überbucht!
Die Lösung liegt fast immer in der Datenbank, nicht im Anwendungscode. Eine atomare Aktualisierung wie „verringere die freien Plätze um eins, aber nur, wenn sie größer als null sind“ prüft und ändert in einem einzigen Schritt. Alternativ sperrt eine Transaktion die betroffene Zeile, bis sie abgeschlossen ist. Eindeutige Regeln in der Datenbank, etwa dass ein Termin nur einmal vergeben werden kann, bieten eine letzte Sicherung. Prüfungen im Anwendungscode allein – „ist noch ein Platz frei? Dann buche!“ – reichen nicht, sobald mehrere Anfragen gleichzeitig laufen, und erst recht nicht, wenn mehrere Server beteiligt sind. Das Modul zur Datenhaltung vertieft Transaktionen und Isolationsstufen.
5. Weitere Fallen
Neben Race Conditions gibt es weitere typische Probleme. Ein Deadlock entsteht, wenn zwei Vorgänge jeweils auf eine Ressource warten, die der andere hält – beide warten ewig. Datenbanken erkennen das meist und brechen einen der Vorgänge ab; die Anwendung sollte dann erneut versuchen. Erschöpfte Pools entstehen, wenn alle Datenbankverbindungen belegt sind, etwa weil langsame Abfragen sie blockieren; neue Anfragen warten dann, bis ein Zeitlimit greift. Und unbegrenzte Parallelität kann ein System überlasten: Wer für jede eingehende Anfrage hundert parallele Aufrufe an einen externen Dienst startet, erzeugt bei Last eine Lawine. Begrenzungen, sogenannte Semaphoren oder Concurrency Limits, schützen davor.
| Problem | Symptom | Gegenmaßnahme |
|---|---|---|
| Race Condition | Überbuchungen, falsche Zähler | atomare Updates, Sperren, eindeutige Regeln |
| Deadlock | Anfragen hängen, Datenbank bricht ab | feste Reihenfolge beim Sperren, kurze Transaktionen, Wiederholen |
| Blockierter Event Loop | alle Anfragen werden langsam | Rechenarbeit auslagern |
| Erschöpfter Pool | Wartezeiten trotz geringer CPU-Last | Poolgröße anpassen, langsame Abfragen beheben, Zeitlimits |
| Unbegrenzte Parallelität | Überlast bei Abhängigkeiten | Concurrency Limits, Warteschlangen |
6. Was das für den Entwurf bedeutet
Aus diesen Grundlagen ergeben sich einige Regeln, die in den folgenden Modulen immer wieder auftauchen. Anwendungsserver sollten zustandslos sein: Sie halten keine Daten im Arbeitsspeicher, die nach einem Neustart oder auf einem anderen Server fehlen würden. Dann lassen sich beliebig viele Instanzen parallel betreiben. Alles, was dauerhaft und für alle gleich sein muss, liegt in der Datenbank, und Regeln, die nie verletzt werden dürfen, werden dort durchgesetzt. Rechenintensive oder langsame Aufgaben gehen in Warteschlangen. Und jede Ressource – Verbindungen, parallele Aufrufe, Speicher – bekommt eine Obergrenze. Wer diese Regeln befolgt, baut Systeme, die unter Last vorhersehbar reagieren, statt auf überraschende Weise zu versagen. Im nächsten Modul geht es darum, wie Daten sicher und effizient gespeichert werden.
Ein praktischer Tipp zum Abschluss: Beobachten Sie in Ihrer eigenen Anwendung, wo Anfragen Zeit verbringen. Viele Frameworks und Überwachungswerkzeuge zeigen für jede Anfrage, wie lange sie auf die Datenbank, auf externe Dienste oder auf Rechenarbeit gewartet hat. Häufig zeigt sich, dass eine einzelne langsame Abfrage oder ein vergessenes Zeitlimit mehr Probleme verursacht als jede Frage nach der richtigen Programmiersprache. Die Wahl zwischen Node.js, Python oder Go ist selten entscheidend; entscheidend ist, dass man die Eigenschaften der gewählten Laufzeit kennt und die Architektur darauf abstimmt. Wer weiß, dass sein Event Loop nicht blockieren darf, dass Python-Threads nicht parallel rechnen und dass die Datenbank die letzte Instanz für Konsistenz ist, trifft in den folgenden Modulen bessere Entscheidungen – von der Wahl der Datenbank bis zur Gestaltung von Warteschlangen und Workern.
- [1]Tanenbaum, A. S.; Bos, H. (2022): Modern Operating Systems. 5. Aufl. Hoboken: Pearson.
- [2]Node.js Foundation (2026): Don't Block the Event Loop (or the Worker Pool). nodejs.org.
- [3]Python Software Foundation (2024): PEP 703 – Making the Global Interpreter Lock Optional in CPython.
- [4]Kleppmann, M. (2017): Designing Data-Intensive Applications. Sebastopol: O'Reilly, Kap. 7 (Transactions).
- [5]PostgreSQL Global Development Group (2026): Documentation – Explicit Locking; Concurrency Control.
Stand: September 2026. Kursmaterial der Klarwerk Akademie. Zahlen zu Latenzen und Kosten sind Größenordnungen zur Orientierung, keine Messwerte.