Thread-Pool: Threads wiederverwenden
Kurz: Eine feste Gruppe vorbereiteter Threads, die Aufgaben aus einer Warteschlange abarbeiten, statt für jede Aufgabe einen neuen Thread zu starten.
Genauer: Das Erzeugen eines Threads kostet Zeit und Speicher. Wer für jede kleine Aufgabe einen neuen Thread startet, verschwendet Ressourcen und kann bei vielen gleichzeitigen Aufgaben das System überlasten. Ein Thread-Pool hält eine begrenzte Zahl Arbeiter bereit und legt eingehende Aufgaben in eine Warteschlange.
Im Detail
Funktionsweise
- Beim Start werden N Worker-Threads angelegt.
- Neue Aufgaben kommen in eine Aufgabenwarteschlange.
- Ein freier Worker nimmt die nächste Aufgabe, führt sie aus und holt danach die nächste. Sind alle Worker belegt, wartet die Aufgabe in der Schlange. So bleibt die Zahl gleichzeitiger Threads begrenzt.
Beispiel in Java
ExecutorService pool = Executors.newFixedThreadPool(4);
Future<Integer> ergebnis = pool.submit(() -> rechne());
System.out.println(ergebnis.get());
pool.shutdown();Python bietet concurrent.futures.ThreadPoolExecutor, Go setzt auf leichtgewichtige Goroutinen, und Node.js nutzt für Dateizugriffe intern einen Pool.
Die richtige Größe
Bei rechenintensiven Aufgaben passt etwa die Zahl der CPU-Kerne. Bei Aufgaben, die oft auf Netzwerk oder Festplatte warten, kann der Pool deutlich größer sein, da die Threads meist schlafen. Eine Faustformel lautet: Threads ≈ Kerne × (1 + Wartezeit / Rechenzeit). Messungen schlagen jede Formel.
Fallstricke
Eine unbegrenzte Warteschlange kann bei Überlast den Speicher füllen; besser ist eine begrenzte Schlange mit einer Regel, was bei Überlauf passiert (ablehnen, im Aufrufer ausführen). Aufgaben, die aufeinander warten, können einen Pool verklemmen (Deadlock), wenn alle Worker blockiert sind. Fehler in Aufgaben müssen behandelt werden, sonst gehen sie unbemerkt verloren. Pools beim Beenden sauber herunterfahren.
Siehe auch: Thread, Nebenläufigkeit, Deadlock, Warteschlange