Ein paar Hintergrundinformationen dazu, wer hier schreibt und warum. Wir sind Geonode und verkaufen Proxys, wodurch wir ständig mit diesem Thema zu tun haben – Web-Scraping ist die klassische I/O-gebundene Arbeitslast, und „Mein Scraper ist langsam“ ist eines der häufigsten Probleme, mit denen sich Nutzer an uns wenden. Hier also die ehrliche Antwort, bevor wir zur Theorie kommen: Wenn Ihr Scraper langsam ist, weil er jeweils nur eine Seite abfragt, wird der Kauf von Proxys ihn nicht beschleunigen. Parallelität ist eine Eigenschaft Ihres Codes. Hundert Proxy-Endpunkte und eine sequenzielle Schleife ergeben einen sequenziellen Scraper mit hundert ungenutzten Endpunkten. Behebe zuerst das Problem der Parallelität. Proxys lösen ein anderes Problem – nämlich das, auf das du stößt, nachdem die Parallelität funktioniert, wenn das Ziel eine Ratenbegrenzung für die Adresse einführt, die plötzlich fünfzig Anfragen pro Sekunde stellt. Beide Probleme sind real. Sie sind jedoch nicht dasselbe Problem und haben auch nicht dieselbe Lösung.
Nachdem das gesagt ist, nun zur Unterscheidung.
Der Unterschied in einem Satz
Rob Pikes Formulierung aus seinem Vortrag zu diesem Thema ist nach wie vor die klarste, und im Go-Blog wird dies direkt dargelegt:
Parallelität ist „die Zusammensetzung von unabhängig voneinander ausgeführten Prozessen“.
Parallelität ist „die gleichzeitige Ausführung von (möglicherweise miteinander verbundenen) Berechnungen“.
Lest euch das zweimal durch, denn der Unterschied liegt darin, worauf der Schwerpunkt liegt. Bei der Parallelität geht es um die Struktur – darum, wie man ein Problem in Teile zerlegt, die unabhängig voneinander ablaufen können. Bei der Parallelität geht es um die Ausführung – darum, wie viele dieser Teile physisch im selben Augenblick laufen.
Die Konsequenz, die oft übersehen wird: Konkurrenz ist etwas, das man programmiert; Parallelität ist etwas, das die Maschine leistet. Man kann ein konkurrentes Programm schreiben und es auf einem einzigen Kern ausführen, wo nichts jemals gleichzeitig geschieht, und es wird dennoch konkurrierend sein. Man hat unabhängige Aufgaben beschrieben; die Laufzeitumgebung verschachtelt sie. Und ein auf acht Kernen ausgeführtes konkurrentes Programm kann parallel werden, sofern die Laufzeitumgebung und die Arbeitslast dies zulassen.
Diese Asymmetrie ist der Grund, warum die beiden Begriffe nicht austauschbar sind. Konkurrenz ermöglicht Parallelität, ohne sie zu garantieren. Parallelität ohne konkurrente Struktur ist überhaupt nicht möglich.
Warum die Verwirrung anhält
Drei Gründe – und es hilft, sie beim Namen zu nennen.
Das beobachtbare Verhalten ist oft identisch. Ein konkurrentes Programm auf einem Kern und ein paralleles Programm auf vier Kernen sehen beide so aus, als würden „mehrere Dinge gleichzeitig passieren“. Von außen lässt sich nicht erkennen, um welches es sich handelt. Man merkt es erst, wenn man weitere Kerne hinzufügt und sich nichts verbessert.
Der Wortschatz der einzelnen Sprachen ist uneinheitlich. Das Python-Modul „threading“ bietet Ihnen Konkurrenz, historisch gesehen jedoch keinen Parallelismus. „multiprocessing“ in Python bietet Ihnen beides. „async“ bzw. „await“ in JavaScript bieten Ihnen Konkurrenz, aber niemals Parallelismus für Ihren eigenen Code. Die Goroutinen in Go bieten Ihnen Konkurrenz und Parallelismus bis zu GOMAXPROCS. Dieselben Begriffe in der Dokumentation haben in verschiedenen Ökosystemen unterschiedliche Bedeutungen.
Meistens muss man sich darüber keine Gedanken machen – bis es plötzlich doch darauf ankommt. Bei einer I/O-gebundenen Arbeitslast ist der Unterschied fast rein theoretisch – schon die Konkurrenz allein bringt den gesamten Gewinn. Bei einer CPU-gebundenen Arbeitslast ist das entscheidend, denn Konkurrenz allein bringt dir nichts. Das Problem ist, dass Leute das Muster lernen, das bei ihrem I/O-gebundenen Problem funktioniert hat, und es dann auf ein CPU-gebundenes Problem anwenden.
Wie es in den einzelnen Sprachen tatsächlich funktioniert
| Laufzeitumgebung | Mechanismus zur Parallelität | Echte Parallelität für Ihren Code | Wo es nicht funktioniert |
|---|
| Python (Standard-Build) | threading, asyncio | Nur über multiprocessing | GIL serialisiert die Bytecode-Ausführung |
| Python (Free-Threaded-Build) | threading, asyncio | Ja, Threads laufen parallel | Single-Thread-Overhead, Reife des Ökosystems |
| Go | Goroutinen + Kanäle | Ja, bis zu GOMAXPROCS | Gemeinsamer Zustand erfordert weiterhin Synchronisation |
| Node.js | Ereignisschleife, async/await | Nur über worker_threads oder untergeordnete Prozesse | Ein CPU-gebundener Callback blockiert alles |
| Java / C# | Threads, Thread-Pools | Ja | Komplexität des gemeinsam genutzten, veränderbaren Zustands |
| Rust | async + Threads | Ja | Der Compiler zwingt dich dazu, von vornherein korrekt zu programmieren |
Node.js ist das anschaulichste Beispiel für Parallelität ohne Parallelismus. Die offizielle Dokumentation bringt es auf den Punkt: Die Ereignisschleife „ermöglicht es Node.js, nicht blockierende E/A-Operationen durchzuführen – trotz der Tatsache, dass standardmäßig ein einziger JavaScript-Thread verwendet wird –, indem Operationen, wann immer möglich, an den Systemkern ausgelagert werden.“ Der Kernel ist multithreaded; Ihr JavaScript hingegen nicht. Die Schleife durchläuft sechs Phasen – Timer, ausstehende Callbacks, Leerlauf/Vorbereitung, Abfrage, Überprüfung, Schließen von Callbacks – und wenn eine Operation abgeschlossen ist, „teilt der Kernel dies Node.js mit, damit der entsprechende Callback zur Abfragewarteschlange hinzugefügt und schließlich ausgeführt werden kann.“
Die praktische Konsequenz ergibt sich direkt daraus. Zehntausend gleichzeitige HTTP-Anfragen in Node sind kein Problem, da das Warten im Kernel stattfindet. Eine einzige CPU-gebundene Funktion, die zwei Sekunden lang läuft, lässt den gesamten Prozess erstarren, da es nur einen Thread gibt, auf dem sie ausgeführt werden kann, und nichts sie unterbrechen kann.
Go verfolgt den gegenteiligen Ansatz: Goroutinen lassen sich kostengünstig zu Tausenden erstellen, und der Scheduler verteilt sie über Betriebssystem-Threads bis zu „GOMAXPROCS“, was standardmäßig der Anzahl der verfügbaren Kerne entspricht. Go bietet Ihnen also Parallelität und Parallelverarbeitung über dasselbe Konstrukt. Aus diesem Grund hielt Pike den Vortrag – die Unterscheidung ist besonders wichtig in einer Sprache, in der man beides erhält und sie daher leicht verwechseln kann.
Die entscheidende Frage: Ist Ihre Anwendung I/O-gebunden oder CPU-gebunden?
Alles oben Gesagte lässt sich auf eine einzige Frage zu Ihrer Arbeitslast reduzieren, und es lohnt sich, diese zu messen, anstatt Annahmen zu treffen.
I/O-gebunden bedeutet, dass Ihr Programm den Großteil seiner Zeit mit Warten verbringt: auf eine Netzwerkantwort, einen Festplattenzugriff oder eine Datenbankabfrage. Während der Wartezeit ist die CPU im Leerlauf. Parallelität ist die richtige und ausreichende Lösung, denn sie ermöglicht es Ihnen, die nächste Wartephase zu starten, während die aktuelle noch andauert. Parallelität bringt im Grunde keinen zusätzlichen Nutzen – acht Kerne, die auf das Netzwerk warten, sind nicht schneller als ein Kern, der auf das Netzwerk wartet.
CPU-gebunden bedeutet, dass Ihr Programm den Großteil seiner Zeit mit Berechnungen verbringt: Parsen, Komprimieren, Hashen, Transformieren. Die CPU ist ausgelastet. Parallelität allein ändert nichts – das Verschachteln zweier Berechnungen auf einem Kern dauert insgesamt genauso lange wie deren sequenzielle Ausführung, zuzüglich des Overheads für den Wechsel. Nur Parallelität hilft, und zwar nur bis zur Anzahl der physischen Kerne.
Um herauszufinden, was bei Ihnen der Fall ist, sollten Sie messen statt zu spekulieren. Unter Linux liefert der Befehl „time“ sofort die Antwort: Vergleichen Sie die tatsächlich verstrichene Zeit mit der Summe aus Benutzer- und System-CPU-Zeit. Wenn die tatsächliche Zeit deutlich größer ist als die CPU-Zeit, wartest du – du bist E/A-gebunden. Wenn sie nahe beieinander liegen, rechnest du – du bist CPU-gebunden.
Web-Scraping ist ein nützliches Beispiel, da es nacheinander beides umfasst. Das Abrufen von Seiten ist stark E/A-gebunden; das anschließende Parsen des HTML-Codes ist CPU-gebunden. Die richtige Architektur nutzt Parallelität beim Abrufen und Parallelität beim Parsen; ein häufiger Fehler ist es, für beide Phasen dieselbe Strategie anzuwenden. Ein Scraper mit 200 gleichzeitigen Abrufen, die einen Single-Thread-Parser versorgen, ist kein schneller Scraper – er ist ein schneller Abrufer, hinter dem sich eine Warteschlange staut.
Pythons GIL und was sich durch „Free Threading“ geändert hat
Python verdient einen eigenen Abschnitt, da sich die Situation hier grundlegend geändert hat und vieles, was Sie darüber lesen werden, mittlerweile veraltet ist.
Historisch gesehen: Das Global Interpreter Lock (GIL) von CPython erlaubte jeweils nur einem Thread die Ausführung von Python-Bytecode. Threads ermöglichten daher zwar Konkurrenz, aber keine Parallelität. Das GIL wird bei E/A-Operationen freigegeben, sodass Thread-basierte E/A problemlos funktionierte; bei CPU-gebundenem Threading war dies jedoch nicht der Fall, und „multiprocessing“ war die Umgehungslösung.
Was sich geändert hat: Ab der Version 3.13 liefert CPython einen optionalen Build mit deaktiviertem GIL aus. Die Dokumentation zu Free-Threading beschreibt dies klar und deutlich: „Die Ausführung mit Free-Threading ermöglicht die volle Ausnutzung der verfügbaren Rechenleistung, indem Threads parallel auf den verfügbaren CPU-Kernen ausgeführt werden.“
PEP 779, die am 16. Juni 2025 vom Lenkungsausschuss mit dem Status „Final“ angenommen wurde, legte die Kriterien für den Übergang von Free-Threading vom experimentellen in den offiziell unterstützten Status fest, wobei Python 3.14 als Ziel für diese Phase vorgesehen war.
Vier praktische Hinweise, bevor Sie diese Funktion nutzen:
Es handelt sich nicht um die Standardversion. Sie müssen sie gezielt beschaffen oder kompilieren – aus dem Quellcode, d. h. mit der Konfigurationsoption „--disable-gil“. Überprüfen Sie, welche Version Sie ausführen, mit dem Befehl „python -VV“, der „free-threading build“ anzeigt, oder mit „sys._is_gil_enabled()“, der „False“ zurückgibt, wenn der GIL deaktiviert ist.
Single-Thread-Code wird langsamer. Die Dokumentation berichtet, dass in der pyperformance-Suite „der durchschnittliche Overhead zwischen etwa 1 % auf macOS aarch64 und 8 % auf x86-64-Linux-Systemen liegt“. PEP 779 hält fest, dass der Lenkungsausschuss davon ausgeht, dass „Free-Threaded“-Python „etwa 10–15 % langsamer sein wird“, wobei 15 % als festes Ziel für Phase II gelten, und akzeptiert einen Anstieg des Speicherverbrauchs um den geometrischen Mittelwert von 20 % als „den Preis für effizientes und sicheres Free-Threading“. Wenn Ihr Programm single-threaded ist, stellt dieser Build eine klare Verschlechterung dar.
Sie können den GIL zur Laufzeit wieder aktivieren. Free-Threaded-Builds unterstützen die Ausführung mit aktiviertem GIL über die Umgebungsvariable PYTHON_GIL oder die Option -X gil – nützlich, wenn eine Abhängigkeit Fehlverhalten zeigt.
Ihre Abhängigkeiten sind die Einschränkung. C-Erweiterungen müssen so kompiliert werden, dass sie die Unterstützung für Free-Threading deklarieren. Das Ökosystem hat sich erheblich weiterentwickelt, aber „es läuft auf meinem Rechner mit reinem Python“ ist nicht dasselbe wie „mein wissenschaftlicher Stack funktioniert“.
Für den I/O-gebundenen Fall, der beim Scraping und bei der API-Arbeit vorherrscht, ändert nichts davon Ihre Entscheidung: „asyncio“ oder ein Thread-Pool im Standard-Build bietet Ihnen bereits alles, was Parallelität leisten kann. Free-Threading ist dann von Bedeutung, wenn die Parsing-Phase – und nicht die Abrufphase – Ihr Engpass ist.
Ein Beispiel: Abrufen von 10.000 URLs
Anhand konkreter Zahlen wird der Unterschied deutlich. Nehmen wir an, jede Anfrage dauert 200 ms und das Parsen jeder Antwort beansprucht 50 ms CPU-Zeit auf einem Rechner mit vier Kernen.
Sequenziell. 10.000 × 250 ms = 2.500 Sekunden, etwa 42 Minuten. Die CPU ist während 80 % dieser Zeit im Leerlauf.
Paralleles Abrufen, sequentielles Parsen. Bei 100 parallelen Anfragen sinkt die Abrufzeit auf etwa 20 Sekunden Realezeit. Das Parsen bleibt unverändert: 10.000 × 50 ms = 500 Sekunden. Insgesamt etwa 520 Sekunden, also rund 9 Minuten. Eine 4,8-fache Verbesserung – und beachten Sie, wohin die Zeit geflossen ist. Das Abrufen machte 80 % der ursprünglichen Laufzeit aus und beträgt nun 4 % der neuen Laufzeit. Das Parsen, das Sie nicht verändert haben, macht nun 96 % der Gesamtzeit aus.
Paralleles Abrufen, paralleles Parsen auf vier Kernen. Das Parsen sinkt auf etwa 125 Sekunden. Insgesamt etwa 145 Sekunden, also rund 2,5 Minuten. Eine 17-fache Verbesserung gegenüber der sequenziellen Ausführung.
Aus diesen Zahlen lassen sich drei Erkenntnisse ableiten.
Erstens: Der größte Gewinn ergibt sich aus der Behebung des E/A-Engpasses durch Parallelität, und das fast ohne Aufwand – keine zusätzlichen Kerne, keine Probleme mit gemeinsam genutzten Zuständen, nur eine andere Schleife.
Zweitens: Sobald man den dominierenden Engpass beseitigt hat, rückt sofort der nächste in den Vordergrund. Das ist das Amdahlsche Gesetz in seiner praktischsten Form: Die Optimierung einer Phase, die 20 % der Laufzeit ausmacht, kann die Geschwindigkeit nicht um mehr als 25 % steigern, egal wie vollständig man sie beseitigt. Messen Sie immer vor der Optimierung und messen Sie danach erneut, denn die Antwort ändert sich.
Drittens – und hier kommt unser kommerzielles Interesse ins Spiel, also wäge dies entsprechend ab – in dem Moment, in dem du von einer Anfrage nach der anderen auf hundert übergehst, wirst du sichtbar. Eine einzelne IP-Adresse, die 500 Anfragen pro Sekunde an einen Host sendet, wird rate-limited und anschließend blockiert. Das ist kein Problem der Parallelität, und kein noch so ausgefeiltes „asyncio“ kann es beheben; es ist ein Verteilungsproblem, und genau dafür sind Proxys da. Unser Traffic über Privathaushalte beginnt bei 0,79 $/GB und der über Rechenzentren bei 0,14 $/GB, Stand September 2026 gemäß unserer Preisseite. Beachten Sie jedoch die Reihenfolge: Zuerst Parallelität, dann Proxys, wenn die Parallelität ein Problem verursacht, das sie nicht lösen kann. Es umgekehrt zu machen bedeutet, für Bandbreite zu bezahlen, die Sie gar nicht nutzen können.
Wo Parallelität ihren Nutzen verliert
Eine weitere Steigerung der Parallelität führt zu abnehmenden und schließlich negativen Erträgen, und der Wendepunkt tritt früher ein, als die meisten erwarten.
Verbindungsbeschränkungen. Betriebssysteme begrenzen die Anzahl offener Dateideskriptoren. Server begrenzen die Anzahl gleichzeitiger Verbindungen pro Client. Zehntausend gleichzeitige Anfragen von einem Rechner stoßen auf eine dieser Obergrenzen, lange bevor die CPU-Kapazität erschöpft ist, und der Fehlermodus äußert sich meist eher in einem verwirrenden als in einem eindeutigen Fehler.
Arbeitsspeicher. Jede laufende Anfrage hält Puffer, geparste Header und ausstehende Antwortdaten bereit. Zehntausend gleichzeitige Anfragen mit jeweils 100 KB bedeuten ein Gigabyte Arbeitsspeicher, der nichts anderes tut, als zu warten.
Overhead durch Kontextwechsel. Betriebssystem-Threads sind nicht kostenlos – jeder ist mit einem Stack und Scheduling-Kosten verbunden. Genau aus diesem Grund gibt es Goroutinen und Coroutinen: Sie sind so ressourcenschonend, dass Tausende davon sinnvoll sind, während Tausende von Betriebssystem-Threads dies nicht sind.
Die Toleranz des Zielservers. Das andere Ende der Verbindung hat seine eigenen Vorstellungen. Ab einer bestimmten Rate führt zusätzliche Parallelität eher zu 429- und 503-Fehlern als zu Daten, und Ihr effektiver Durchsatz sinkt, je mehr Sie hinzufügen. Dies ist die häufigste Obergrenze in der Praxis und diejenige, die am seltensten gemessen wird, da die Anfragen immer noch „funktionieren“ – sie geben lediglich Fehler zurück, die eine Wiederholungsschleife pflichtbewusst wiederholt.
Der praktische Ansatz ist wenig glamourös: Beginnen Sie mit einer moderaten Parallelitätsgrenze, messen Sie die Anzahl der abgeschlossenen Anfragen pro Sekunde statt der Anzahl der versuchten Anfragen und erhöhen Sie den Wert, bis sich der Durchsatz nicht mehr verbessert. Er wird sich einpendeln und dann zurückgehen. Das Optimum liegt bei diesem Plateau, und es handelt sich in der Regel um eine viel kleinere Zahl, als die Intuition vermuten lässt – oft um Zehner statt um Hunderter.
Wenn beides nicht nötig ist
Das ist erwähnenswert, da „es parallel ausführen“ mittlerweile zum Reflex geworden ist.
Wenn der Arbeitsaufwand wirklich gering ist. Hundert Anfragen, die jeweils 200 ms dauern, ergeben sequenziell 20 Sekunden. Wenn dies nächtlich in einem Cron-Job läuft, sind 20 Sekunden völlig in Ordnung, und paralleler Code ist schwieriger zu debuggen, wenn er um 3 Uhr morgens ausfällt.
Wenn die Reihenfolge Teil der Anforderung ist. Manche Pipelines müssen Elemente in einer strengen Reihenfolge verarbeiten, oder jeder Schritt hängt vom vorherigen Ergebnis ab. Parallelität ist hier nicht nur nutzlos, sondern eine Quelle für Fehler, die nur unter Last auftreten.
Wenn der Engpass ganz woanders liegt. Wenn Ihre Datenbank-Schreibvorgänge die Einschränkung darstellen, verursachen 200 gleichzeitige Leser lediglich eine längere Warteschlange vor derselben Sperre. Beheben Sie den tatsächlichen Engpass. Parallelität vor einer seriellen Ressource verwandelt ein langsames Programm in ein langsames Programm mit einem Speicherproblem.
Wenn der gemeinsam genutzte Zustand kompliziert ist. Paralleler Code, der auf einen gemeinsam genutzten, veränderbaren Zustand zugreift, erfordert Synchronisation, und wenn man dabei Fehler macht, entstehen die schlimmsten Fehler – sporadisch auftretend, lastabhängig und auf Ihrem Rechner nicht reproduzierbar. Wenn die Beschleunigung das Zweifache beträgt und der Zustand komplex ist, ist sequenzieller Code, den man nachvollziehen kann, häufig die bessere technische Entscheidung.
Und die für uns relevante Variante: Wenn Sie täglich ein paar hundert Seiten von einer Website scrapen, der das nichts ausmacht, brauchen Sie weder Parallelität noch Proxys. Ein „requests“-Aufruf in einer Schleife mit einer angemessenen Verzögerung ist die richtige Lösung, und das sagen wir Ihnen lieber, als Ihnen ein Paket zu verkaufen, das Sie nicht brauchen.
Häufig gestellte Fragen
Was ist der einfachste Unterschied zwischen Parallelität und Parallelität?
Parallelität bedeutet, viele Dinge gleichzeitig zu behandeln – eine strukturelle Eigenschaft der Art und Weise, wie man das Programm schreibt. Parallelität bedeutet, viele Dinge gleichzeitig auszuführen – eine physikalische Eigenschaft der Art und Weise, wie es ausgeführt wird. Konkurrenz ermöglicht Parallelität; sie bewirkt sie jedoch nicht.
Kann es Parallelität ohne Konkurrenz geben?
Nicht in dem hier besprochenen Sinne. Die parallele Ausführung erfordert unabhängige Arbeitseinheiten, die verteilt werden können, und genau diese Einheiten zu definieren, ist der Sinn der Parallelität. Parallelität auf Hardware-Ebene wie SIMD ist eine Ausnahme – sie parallelisiert einen einzelnen Befehlsstrom über Daten hinweg, ohne dass eine parallele Struktur im Programm vorhanden ist.
Gibt es den Python-GIL noch?
Ja, in der Standardversion. Seit Version 3.13 liefert CPython auch eine optionale „Free-Threaded“-Version mit deaktiviertem GIL aus, und PEP 779 hat diese Version mit Blick auf Version 3.14 in den Status „offiziell unterstützt“ überführt. Die Standardversion verfügt weiterhin über den GIL; sofern Sie also nicht bewusst einen „Free-Threaded“-Interpreter installiert haben, ist der GIL vorhanden.
Ist „async“ dasselbe wie Multithreading?
Nein. Asynchrone Parallelität nutzt einen einzigen Thread mit kooperativem Wechsel an expliziten „await“-Punkten, sodass jeweils nur ein Teil Ihres Codes ausgeführt wird und Wechsel nur dort stattfinden, wo Sie sie geschrieben haben. Multithreading nutzt mehrere Betriebssystem-Threads mit präemptivem Wechsel, der an beliebiger Stelle erfolgen kann. Asynchrone Parallelität ist leichter nachzuvollziehen; Threads können echte Parallelität erreichen, sofern die Laufzeitumgebung dies zulässt.
Wie viele gleichzeitige Anfragen sollte ich stellen?
Weniger, als Sie denken. Beginnen Sie mit etwa 10, messen Sie die Anzahl der abgeschlossenen Anfragen pro Sekunde und erhöhen Sie die Anzahl, bis diese Zahl nicht mehr steigt. Die Obergrenze liegt in der Regel eher in der Belastbarkeit des Zielservers als in der Kapazität Ihres Rechners, und jenseits dieses Punktes führt zusätzliche Parallelität eher zu Fehlern als zu mehr Durchsatz.
Macht Parallelität meinen Code schneller?
Nur, wenn Sie auf etwas warten. Bei I/O-gebundenen Aufgaben sind die Gewinne groß. Bei CPU-gebundenen Aufgaben auf einem einzelnen Kern verlangsamt Parallelität den Ablauf aufgrund des Umschalt-Overheads geringfügig – Sie benötigen Parallelität, was mehrere Kerne und eine Laufzeitumgebung voraussetzt, die diese nutzen kann.
Was ist der Unterschied zwischen Multiprocessing und Multithreading?
Threads teilen sich den Speicher innerhalb eines Prozesses, was die Kommunikation kostengünstig macht, den gemeinsamen Zustand jedoch gefährlich. Prozesse verfügen über separaten Speicher, was sie sicher macht, die Kommunikation jedoch aufwendig. In der Standardversion von Python erzielen Sie mit Prozessen echte Parallelität bei CPU-gebundenen Aufgaben; Threads bieten Ihnen Parallelität bei E/A-gebundenen Aufgaben.
Benötige ich Proxys, um parallele Anfragen auszuführen?
Nicht zwangsläufig. Sie benötigen sie, wenn Sie durch die Parallelität so auffällig werden, dass ein Ziel die Zugriffsrate drosselt oder die Adresse blockiert, von der aus Sie zugreifen. Bei einer API mit großzügigem Kontingent oder einer Website, für deren Crawling Sie die Erlaubnis haben, reicht Parallelität allein aus. Bei einer Website, die pro Adresse Beschränkungen auferlegt, wird die Verteilung zum limitierenden Faktor – und das ist eine separate Maßnahme, die über die Optimierung Ihres Codes hinausgeht.
Fazit
Es lohnt sich, diese Unterscheidung im Auge zu behalten, denn sie verwandelt eine vage Frage – „Wie kann ich das beschleunigen?“ – in eine konkrete Frage mit einer überprüfbaren Antwort: Warte ich gerade, oder führe ich Rechenoperationen durch?
Wenn Sie warten, brauchen Sie Parallelität, und zwar in welcher Form auch immer Ihre Sprache diese bereitstellt. Der Gewinn ist groß, es entstehen in der Regel keine zusätzlichen Hardwarekosten, und sie ist in jeder gängigen Laufzeitumgebung verfügbar. Wenn Sie Rechenoperationen ausführen, nützt Ihnen Parallelität allein nichts, und Sie benötigen echte Parallelität – Prozesse, Worker-Threads, Goroutinen über mehrere Kerne hinweg oder im Fall von Python möglicherweise einen Free-Threaded-Interpreter mit seinen eigenen Vor- und Nachteilen, die es abzuwägen gilt.
Die meisten realen Programme weisen in verschiedenen Phasen beide Aspekte auf, und die Reihenfolge ist wichtiger als die Wahl. Beheben Sie den dominierenden Engpass, messen Sie erneut, und rechnen Sie damit, dass sich das Ergebnis verschoben hat. Eine Pipeline, die zu 80 % netzwerkgebunden war, wird in dem Moment, in dem Sie das Netzwerkproblem beheben, zu 96 % parsegebunden, und die zweite Optimierung ist eine völlig andere Aufgabe als die erste.