Viele Entwickler setzen zuerst auf Threads – und wundern sich dann über miese Speedups. Besonders in Python bremst der GIL (Global Interpreter Lock) CPU-lastige Aufgaben aus. Multiprocessing nutzt mehrere Prozesse statt Threads und umgeht so den GIL. Kurz gesagt: CPU-intensive Arbeit skaliert meist besser mit Prozessen.
Frag dich: ist die Aufgabe CPU-gebunden oder I/O-gebunden? Wenn du große Berechnungen, Bildverarbeitung oder numerische Simulationen hast, bringt Multiprocessing echten Gewinn. Bei Netzwerk-Requests oder Dateizugriffen sind Threads oder async oft einfacher und ressourcenschonender. Prozesse isolieren Speicher – Abstürze sind weniger dramatisch, aber der Overhead ist höher.
Ein weiterer Grund für Multiprocessing ist Stabilität: Prozesse laufen unabhängig voneinander. Fällt ein Task aus, bleibt der Hauptprozess intakt. Das hilft in Produktionssystemen, in denen einzelne Jobs fehlschlagen können.
Nutze high-level-APIs wie concurrent.futures.ProcessPoolExecutor oder die multiprocessing.Pool-Klasse. Beide machen das Aufteilen von Jobs einfach. Beispiel: map()-ähnliche Aufrufe verteilen eine Funktion automatisch auf Worker-Prozesse.
Achte auf Picklebarkeit: Argumente und Rückgaben müssen serialisierbar sein. Lambda-Funktionen, lokale Funktionen oder komplexe Objekte können Probleme machen. Pack stattdessen Funktionen in Modul-Level und sende nur einfache Datenstrukturen.
Starte Prozesse richtig: Auf Windows ist der Startmodus 'spawn' Standard, auf Unix häufig 'fork'. Spawn verhindert Side-Effekte beim Import, ist aber langsamer. Verwende multiprocessing.set_start_method() bewusst, wenn du plattformspezifische Probleme hast.
Vermeide großen Datentransfer zwischen Prozessen. Große Arrays lieber in Shared Memory (multiprocessing.shared_memory) oder nutze numpy-mmap, statt Daten per Queue zu verschicken. Copy-on-write beim Fork kann Speicher sparen, aber nur auf Unix.
Debugging: Logs von Child-Prozessen landen nicht automatisch in der Konsole. Schreibe Fehler in Dateien oder leite Logging an den Hauptprozess weiter. Für reproduzierbare Tests setze Seeds und prüfe Timeouts bei langen Tasks.
Pool-Größe: Mehr Prozesse sind nicht immer besser. Starte mit so vielen Prozessen wie Kerne (os.cpu_count()) und messe. I/O-lastige Tasks vertragen oft mehr Worker, CPU-lastige nicht.
Alternativen und Kombinationen: Für gemischte Workloads kombiniere asyncio für I/O mit einem ProcessPoolExecutor für CPU-Last. Oder benutze spezialisierte Bibliotheken wie joblib für Data-Science-Workflows.
Kurz und praktisch: Nutze Multiprocessing bei echten CPU-Flaschenhälsen, achte auf Serialisierung und Startmethoden, und messe regelmäßig. Mit diesen Regeln vermeidest du die häufigsten Fallen und bekommst echten Speedup für deine rechenintensiven Aufgaben.
Was bremst Python 2025 wirklich? Die GIL und Interpreter-Overhead. Wann es dich trifft, wie du es misst und konkrete Wege zu schnellerem Code – mit Praxis, Regeln und Benchmarks.
© 2025. Alle Rechte vorbehalten.