Warum das Global Interpreter Lock?

89

Was genau ist die Funktion von Pythons Global Interpreter Lock? Verwenden andere Sprachen, die zu Bytecode kompiliert wurden, einen ähnlichen Mechanismus?

Federico A. Ramponi
quelle
6
Sie sollten auch fragen: "Ist das überhaupt wichtig?"
S.Lott
2
Ich stimme zu, ich halte es jetzt für kein Problem, da in 2.6 das Multiprocessing-Modul hinzugefügt wurde, damit Sie mit mehreren Prozessen threadartig programmieren können. docs.python.org/library/multiprocessing.html
monkut
Was ist der Gil: stackoverflow.com/questions/1294382/… Bezogen auf Programmierer: softwareengineering.stackexchange.com/questions/186889/…
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功

Antworten:

69

Im Allgemeinen müssen Sie für alle Thread-Sicherheitsprobleme Ihre internen Datenstrukturen mit Sperren schützen. Dies kann mit verschiedenen Granularitätsstufen erfolgen.

  • Sie können eine feinkörnige Verriegelung verwenden, bei der jede einzelne Struktur eine eigene Verriegelung hat.

  • Sie können eine grobkörnige Verriegelung verwenden, bei der eine Verriegelung alles schützt (GIL-Ansatz).

Für jede Methode gibt es verschiedene Vor- und Nachteile. Feinkörniges Sperren ermöglicht eine größere Parallelität - zwei Threads können parallel ausgeführt werden, wenn sie keine Ressourcen gemeinsam nutzen. Es gibt jedoch einen viel größeren Verwaltungsaufwand. Für jede Codezeile müssen Sie möglicherweise mehrere Sperren erwerben und freigeben.

Der grobkörnige Ansatz ist das Gegenteil. Zwei Threads können nicht gleichzeitig ausgeführt werden, aber ein einzelner Thread wird schneller ausgeführt, da er nicht so viel Buchhaltung betreibt. Letztendlich kommt es auf einen Kompromiss zwischen Single-Threaded-Geschwindigkeit und Parallelität an.

Es gab einige Versuche, die GIL in Python zu entfernen, aber der zusätzliche Aufwand für Single-Threaded-Maschinen war im Allgemeinen zu groß. Einige Fälle können sogar auf Multiprozessor-Computern aufgrund von Sperrenkonflikten langsamer sein.

Verwenden andere Sprachen, die zu Bytecode kompiliert wurden, einen ähnlichen Mechanismus?

Es variiert und sollte wahrscheinlich nicht als Spracheigenschaft, sondern als Implementierungseigenschaft betrachtet werden. Beispielsweise gibt es Python-Implementierungen wie Jython und IronPython, die den Threading-Ansatz der zugrunde liegenden VM anstelle eines GIL-Ansatzes verwenden. Darüber hinaus sieht die nächste Version von Ruby zu bewegen in Richtung einer GIL einzuführen.

Brian
quelle
1
Können Sie das erklären: "Zwei Threads können nicht gleichzeitig ausgeführt werden"? Kürzlich habe ich einen einfachen Webserver in Python mit Multithreading geschrieben. Für jede neue Anforderung vom Client erzeugen Server einen neuen Thread für diesen und dieser Thread wird weiterhin ausgeführt. Es werden also mehrere Threads gleichzeitig ausgeführt, oder? Oder habe ich falsch verstanden?
Avi
1
@avi AFAIK Python-Threads können nicht gleichzeitig ausgeführt werden, aber das bedeutet nicht, dass ein Thread den anderen blockieren muss. GIL bedeutet nur, dass nur ein Thread gleichzeitig Python-Code interpretieren kann. Dies bedeutet nicht, dass die Thread-Verwaltung und die Ressourcenzuweisung nicht funktionieren.
Benproductions1
2
^ Zu jedem Zeitpunkt wird nur ein Thread Inhalte für den Client bereitstellen. Es macht also keinen Sinn, tatsächlich Multithreading zu verwenden, um die Leistung zu verbessern. richtig?
Avi
Und natürlich ist Java zu Bytecode kompiliert und ermöglicht eine sehr feinkörnige Sperrung.
Warren Dew
3
@avi, ein E / A-gebundener Prozess wie ein Webserver kann immer noch von Python-Threads profitieren. Zwei oder mehr Threads können gleichzeitig E / A ausführen. Sie können einfach nicht gleichzeitig interpretiert werden (CPU).
Saish
33

Folgendes stammt aus dem offiziellen Python / C-API-Referenzhandbuch :

Der Python-Interpreter ist nicht vollständig threadsicher. Um Python-Programme mit mehreren Threads zu unterstützen, muss der aktuelle Thread eine globale Sperre aufrechterhalten, bevor er sicher auf Python-Objekte zugreifen kann. Ohne die Sperre können selbst die einfachsten Vorgänge Probleme in einem Multithread-Programm verursachen: Wenn beispielsweise zwei Threads gleichzeitig den Referenzzähler desselben Objekts erhöhen, wird der Referenzzähler möglicherweise nur einmal statt zweimal erhöht.

Daher besteht die Regel, dass nur der Thread, der die globale Interpretersperre erhalten hat, Python-Objekte bearbeiten oder Python / C-API-Funktionen aufrufen darf. Um Python-Programme mit mehreren Threads zu unterstützen, gibt der Interpreter die Sperre regelmäßig frei und ruft sie erneut ab - standardmäßig alle 100 Bytecode-Anweisungen (dies kann mit sys.setcheckinterval () geändert werden). Die Sperre wird auch aufgehoben und erneut angefordert, um möglicherweise E / A-Vorgänge wie das Lesen oder Schreiben einer Datei zu blockieren, sodass andere Threads ausgeführt werden können, während der Thread, der die E / A anfordert, auf den Abschluss des E / A-Vorgangs wartet.

Ich denke, es fasst das Problem ziemlich gut zusammen.

Eli Bendersky
quelle
1
Ich habe es auch gelesen, aber ich kann nicht verstehen, warum Python in dieser Hinsicht anders ist als beispielsweise Java (oder?)
Federico A. Ramponi,
@ EliBendersky Python-Threads werden als pthreads implementiert und vom Betriebssystem ( dabeaz.com/python/UnderstandingGIL.pdf ) behandelt, während Java-Threads Threads auf Anwendungsebene sind, deren Planung von der JVM
gokul_uf am
19

Die globale Interpreter-Sperre ist eine große Mutex-Sperre, die Referenzzähler vor dem Abspritzen schützt. Wenn Sie reinen Python-Code schreiben, geschieht dies alles hinter den Kulissen. Wenn Sie jedoch Python in C einbetten, müssen Sie möglicherweise die Sperre explizit aufheben / aufheben.

Dieser Mechanismus hängt nicht damit zusammen, dass Python zu Bytecode kompiliert wird. Es wird für Java nicht benötigt. Tatsächlich wird es nicht einmal für Jython benötigt (Python kompiliert zu jvm).

siehe auch diese Frage

David Nehme
quelle
4
"Dieser Mechanismus hängt nicht damit zusammen, dass Python zu Bytecode kompiliert wird": Genau genommen handelt es sich um ein Artefakt der CPython-Implementierung. Andere Implementierungen (wie Jython, die Sie erwähnt haben) können aufgrund ihrer thread-sicheren Implementierung von dieser Einschränkung befreit sein
Eli Bendersky
11

Python wurde wie Perl 5 nicht von Grund auf als threadsicher entwickelt. Threads wurden nachträglich aufgepfropft, sodass die globale Interpreter-Sperre verwendet wird, um den gegenseitigen Ausschluss aufrechtzuerhalten, wenn zu einem bestimmten Zeitpunkt nur ein Thread Code im Darm des Interpreters ausführt.

Einzelne Python-Threads werden vom Interpreter selbst kooperativ multitasking ausgeführt, indem die Sperre von Zeit zu Zeit gewechselt wird.

Wenn Sie mit Python von C aus sprechen, wenn andere Python-Threads aktiv sind, um sich für dieses Protokoll zu entscheiden und sicherzustellen, dass hinter Ihrem Rücken nichts Unsicheres passiert, müssen Sie die Sperre selbst aktivieren.

Andere Systeme mit einem Single-Thread-Erbe, das sich später zu Mulithread-Systemen entwickelte, verfügen häufig über einen solchen Mechanismus. Zum Beispiel hat der Linux-Kernel die "Big Kernel Lock" aus seinen frühen SMP-Tagen. Mit der Zeit wird die Multithreading-Leistung allmählich zu einem Problem. Daher besteht die Tendenz, diese Art von Sperren in kleinere Teile zu zerlegen oder sie nach Möglichkeit durch sperrfreie Algorithmen und Datenstrukturen zu ersetzen, um den Durchsatz zu maximieren.

Edward KMETT
quelle
+1 für die Erwähnung der Tatsache, dass grobkörnige Verriegelungen verwendet werden, als die meisten denken, insbesondere die oft vergessene BKL (ich benutze reiserfs- der einzige wirkliche Grund, warum ich überhaupt davon weiß).
new123456
3
Linux hatte BKL, seit Version 2.6.39 wurde BKL vollständig entfernt.
Avi
5
Natürlich. Wohlgemerkt, das war ~ 3 Jahre nachdem ich die Frage beantwortet hatte. =)
Edward KMETT
7

In Bezug auf Ihre zweite Frage verwenden dies nicht alle Skriptsprachen, aber es macht sie nur weniger leistungsfähig. Zum Beispiel sind die Threads in Ruby grün und nicht nativ.

In Python sind die Threads nativ und die GIL verhindert nur, dass sie auf verschiedenen Kernen ausgeführt werden.

In Perl sind die Threads noch schlechter. Sie kopieren nur den gesamten Interpreter und sind bei weitem nicht so benutzerfreundlich wie in Python.

Eli Bendersky
quelle
2

Vielleicht hilft dieser Artikel der BDFL.

Jeremy Cantrell
quelle