Unterstützt Python Multithreading? Kann es die Ausführungszeit beschleunigen?

95

Ich bin etwas verwirrt darüber, ob Multithreading in Python funktioniert oder nicht.

Ich weiß, dass es viele Fragen dazu gab und ich habe viele davon gelesen, aber ich bin immer noch verwirrt. Ich weiß aus eigener Erfahrung und habe gesehen, dass andere hier auf StackOverflow ihre eigenen Antworten und Beispiele veröffentlicht haben, dass Multithreading in Python tatsächlich möglich ist. Warum sagen alle immer wieder, dass Python von der GIL gesperrt ist und immer nur ein Thread gleichzeitig ausgeführt werden kann? Es funktioniert eindeutig. Oder gibt es einen Unterschied, den ich hier nicht bekomme?

Viele Poster / Befragte erwähnen auch immer wieder, dass das Threading begrenzt ist, da nicht mehrere Kerne verwendet werden. Aber ich würde sagen, dass sie immer noch nützlich sind, weil sie gleichzeitig arbeiten und somit die kombinierte Arbeitslast schneller erledigen. Ich meine, warum sollte es sonst überhaupt ein Python-Thread-Modul geben?

Aktualisieren:

Vielen Dank für alle bisherigen Antworten. Ich verstehe, dass Multithreading für einige E / A-Aufgaben nur parallel ausgeführt wird, für CPU-gebundene Mehrkernaufgaben jedoch jeweils nur eine.

Ich bin mir nicht ganz sicher, was dies für mich in der Praxis bedeutet, daher gebe ich nur ein Beispiel für die Art von Aufgabe, die ich gerne multithreading möchte. Nehmen wir zum Beispiel an, ich möchte eine sehr lange Liste von Zeichenfolgen durchlaufen und einige grundlegende Zeichenfolgenoperationen für jedes Listenelement ausführen. Wenn ich die Liste aufteile, jede Unterliste, die von meinem Schleifen- / Zeichenfolgencode verarbeitet werden soll, in einem neuen Thread sende und die Ergebnisse in einer Warteschlange zurücksende, werden diese Workloads ungefähr gleichzeitig ausgeführt? Wird dies theoretisch die Zeit beschleunigen, die zum Ausführen des Skripts benötigt wird?

Ein anderes Beispiel könnte sein, wenn ich vier verschiedene Bilder mit PIL in vier verschiedenen Threads rendern und speichern kann und dies schneller ist als die Verarbeitung der Bilder nacheinander? Ich denke, diese Geschwindigkeitskomponente ist das, worüber ich mich wirklich wundere, und nicht die richtige Terminologie.

Ich kenne mich auch mit dem Multiprozessor-Modul aus, aber mein Hauptinteresse gilt derzeit dem Laden kleiner bis mittlerer Aufgaben (10 bis 30 Sekunden). Daher denke ich, dass Multithreading besser geeignet ist, da die Initiierung von Unterprozessen langsam sein kann.

Karim Bahgat
quelle
4
Dies ist eine ziemlich geladene Frage. Ich denke, die Antwort liegt in dem, was die Threads tun sollen. In den meisten Fällen verhindert die GIL, dass mehr als ein Thread gleichzeitig ausgeführt wird. Es gibt jedoch einige Fälle, in denen die GIL freigegeben wird (z. B. Lesen aus einer Datei), sodass dies parallel erfolgen kann. Beachten Sie auch, dass die GIL ein Implementierungsdetail von Cpython ist (die häufigste Implementierung). Keine andere Implementierung von Python (Jython, PyPy usw.) hat eine GIL (AFAIK)
mgilson
2
@mgilson PyPy hat eine GIL.
2
@delnan - Sie scheinen korrekt zu sein. Vielen Dank.
mgilson
1
"Unterprozesse können langsam initiiert werden" - Sie können einen Pool von Aufgaben erstellen, die zur Ausführung bereit sind. Der Overhead kann auf ungefähr die Zeit begrenzt werden, die zum Serialisieren / Deserialisieren der Daten benötigt wird, die erforderlich sind, damit die Aufgabe funktioniert.
Brian Cain
1
@ KarimBahgat, genau das meine ich.
Brian Cain

Antworten:

132

Die GIL verhindert das Einfädeln nicht. Die GIL stellt lediglich sicher, dass jeweils nur ein Thread Python-Code ausführt. Die Steuerung wechselt immer noch zwischen den Threads.

Was die GIL dann verhindert, ist die Verwendung von mehr als einem CPU-Kern oder separaten CPUs, um Threads parallel auszuführen.

Dies gilt nur für Python-Code. C-Erweiterungen können und müssen die GIL freigeben, damit mehrere Threads mit C-Code und ein Python-Thread über mehrere Kerne ausgeführt werden können. Dies erstreckt sich auf vom Kernel gesteuerte E / A, z. B. select()Aufrufe zum Lesen und Schreiben von Sockets, sodass Python Netzwerkereignisse in einem Multithread-Setup mit mehreren Threads relativ effizient verarbeitet.

Viele Serverbereitstellungen führen dann mehr als einen Python-Prozess aus, damit das Betriebssystem die Planung zwischen den Prozessen übernimmt, um Ihre CPU-Kerne maximal zu nutzen. Sie können die multiprocessingBibliothek auch verwenden , um die parallele Verarbeitung über mehrere Prozesse von einer Codebasis und einem übergeordneten Prozess hinweg zu handhaben, wenn dies Ihren Anwendungsfällen entspricht.

Beachten Sie, dass die GIL nur für die CPython-Implementierung gilt. Jython und IronPython verwenden eine andere Threading-Implementierung (die nativen gemeinsamen Java VM- und .NET-Laufzeit-Threads).

So adressieren Sie Ihr Update direkt: Bei jeder Aufgabe, die versucht, durch parallele Ausführung mit reinem Python-Code einen Geschwindigkeitsschub zu erzielen, wird keine Beschleunigung angezeigt, da der Python-Thread-Code an jeweils einen Thread gebunden ist, der ausgeführt wird. Wenn Sie jedoch C-Erweiterungen und E / A einmischen (z. B. PIL- oder Numpy-Operationen), kann jeder C-Code parallel zu einem aktiven Python-Thread ausgeführt werden.

Python-Threading eignet sich hervorragend zum Erstellen einer reaktionsschnellen Benutzeroberfläche oder zum Behandeln mehrerer kurzer Webanforderungen, bei denen E / A der Engpass mehr ist als der Python-Code. Es eignet sich nicht zum Parallelisieren von rechenintensivem Python-Code, zum Festhalten am multiprocessingModul für solche Aufgaben oder zum Delegieren an eine dedizierte externe Bibliothek.

Martijn Pieters
quelle
Vielen Dank an @MartijnPieters, dann habe ich eine klarere Antwort auf meine Frage, ob Threading verwendet werden kann, um Code wie eine for-Schleife zu beschleunigen, die "nein" ist. Vielleicht könnten Sie oder jemand eine neue Antwort schreiben, die ich akzeptieren kann und die einige spezifische Beispiele für gängige Module / Codes / Operationen enthält, bei denen das Threading von der GIL parallel und damit schneller ausgeführt werden kann (z. B. Beispiele für diese E / A und das Netzwerk /). erwähnte Socket-Leseoperationen und alle anderen Fälle, in denen Multithreading in Python nützlich ist). Vielleicht eine schöne Liste gängiger Multithread-Anwendungen und einige Programmierbeispiele, falls möglich?
Karim Bahgat
4
Nein, ich denke nicht, dass eine solche Antwort sehr hilfreich wäre. um ehrlich zu sein. Sie können niemals eine vollständige Liste erstellen, aber als Faustregel gilt, dass alle E / A (Lesen und Schreiben von Dateien, Netzwerksockets, Pipes) in C verarbeitet werden und viele C-Bibliotheken auch die GIL für ihre freigeben Operationen, aber es liegt an den Bibliotheken, dies für Sie zu dokumentieren.
Martijn Pieters
1
Meine schlechte, habe deine aktualisierte Antwort bis jetzt nicht gesehen, wo du einige nette Beispiele für die Verwendung von Threads gegeben hast. Dazu gehörten (korrigieren Sie mich, wenn ich falsch liege ) Netzwerkprogrammierung (z urllib.urlopen(). B. ?), Image.transform()Um ein Python-Skript aus einer Python-GUI heraus aufzurufen und mehrere PIL- (z. B. ) und numpy- (z. B. numpy.array()) Operationen mit Threads aufzurufen . Und Sie haben in Ihrem Kommentar einige weitere Beispiele angegeben, z. B. die Verwendung mehrerer Threads zum Lesen von Dateien (z f.read(). B. ?). Ich weiß, dass eine vollständige Liste nicht möglich ist. Ich wollte nur die Arten von Beispielen, die Sie in Ihrem Update angegeben haben.
Wie auch immer
2
@KarimBahgat: Ja, urllib.urlopen()würde Netzwerk-Sockets aufrufen. Das Warten auf Socket-E / A ist eine hervorragende Gelegenheit, Threads zu wechseln und etwas anderes zu tun.
Martijn Pieters
4
Obwohl es für dieses Problem nicht direkt relevant ist, ist es erwähnenswert, dass es beim Threading manchmal überhaupt nicht um Leistung geht. Es ist möglicherweise einfacher, Ihren Code als mehrere unabhängige Ausführungsthreads zu schreiben. Beispielsweise kann es sein, dass ein Thread Hintergrundmusik abspielt, einer die Benutzeroberfläche bedient und ein Thread Berechnungen durchführt, die eventuell durchgeführt werden müssen, aber nicht in Eile sind. Der Versuch, den nächsten Audiopuffer mit dem UI-Runloop abzuspielen oder die Berechnung in ausreichend kleine Teile zu zerlegen, um die Interaktivität nicht zu beeinträchtigen, ist möglicherweise viel schwieriger als die Verwendung von Threads.
Abarnert
4

Ja. :) :)

Sie haben das Low-Level- Thread- Modul und das High-Level- Threading- Modul. Wenn Sie jedoch einfach Multicore-Maschinen verwenden möchten, ist das Multiprocessing- Modul der richtige Weg.

Zitat aus den Dokumenten :

In CPython kann aufgrund der globalen Interpreter-Sperre nur ein Thread Python-Code gleichzeitig ausführen (obwohl bestimmte leistungsorientierte Bibliotheken diese Einschränkung möglicherweise überwinden). Wenn Sie möchten, dass Ihre Anwendung die Rechenressourcen von Multi-Core-Maschinen besser nutzt, wird empfohlen, Multiprocessing zu verwenden. Threading ist jedoch immer noch ein geeignetes Modell, wenn Sie mehrere E / A-gebundene Aufgaben gleichzeitig ausführen möchten.

Zord
quelle
3

Threading ist in Python zulässig. Das einzige Problem besteht darin, dass die GIL sicherstellt, dass jeweils nur ein Thread ausgeführt wird (keine Parallelität).

Wenn Sie also den Code mit mehreren Threads versehen möchten, um die Berechnung zu beschleunigen, wird er nicht beschleunigt, da jeweils nur ein Thread ausgeführt wird. Wenn Sie ihn jedoch beispielsweise für die Interaktion mit einer Datenbank verwenden, wird dies der Fall sein.

r.guerbab
quelle