In der aktuellen Implementierung von CPython gibt es ein Objekt, das als "GIL" oder "Global Interpreter Lock" bekannt ist. Es ist im Wesentlichen ein Mutex, der verhindert, dass zwei Python-Threads Python-Code zur gleichen Zeit ausführen. Dies verhindert, dass zwei Threads den Status des Python-Interpreters beschädigen können, verhindert aber auch, dass mehrere Threads wirklich zusammen ausgeführt werden. Im Wesentlichen, wenn ich das tue:
%Vor%Ich kann die Liste nicht korrumpieren, weil zu irgendeinem gegebenen Zeitpunkt nur einer dieser Threads ausgeführt wird, da sie die GIL dafür halten müssen. Jetzt können die Elemente in der Liste in einer unbestimmten Reihenfolge hinzugefügt werden, aber der Punkt ist, dass die Liste nicht beschädigt ist und zwei Dinge immer hinzugefügt werden.
Also, jetzt zu C #. C # steht im Wesentlichen vor dem gleichen Problem wie Python. Wie verhindert C # dies? Ich wäre auch daran interessiert, Javas Geschichte zu hören, wenn sie jemand weiß.
Klarstellung: Ich bin interessiert, was passiert, ohne explizite Sperranweisungen, insbesondere für die VM. Ich bin mir bewusst, dass Sperrgrundelemente für Java & amp; C # - sie existieren auch in Python: Die GIL wird nicht für Multi-Thread-Code verwendet, außer um den Interpreter gesund zu halten. Ich interessiere mich für das direkte Äquivalent des Obigen, also, in C #, wenn ich mich genug erinnern kann ...: -)
%Vor%Hier ist ein anderes Beispiel:
%Vor% Ich möchte keinen schlechten C # -Code schreiben, ich verstehe die lock
-Anweisungen. Selbst in Python gibt Ihnen die GIL keinen magischen Multithreading-Code: Sie müssen weiterhin freigegebene Ressourcen sperren. Aber die GIL verhindert, dass Pythons "VM" beschädigt wird - an diesem Verhalten bin ich interessiert.
Die meisten anderen Sprachen, die Threading unterstützen, haben kein Äquivalent zur Python-GIL. Sie müssen Mutexe verwenden, entweder implizit oder explizit.
Wenn Sie die Sperre verwenden, tun Sie Folgendes:
%Vor%und in Thread 2:
%Vor% Die lock
-Anweisung stellt sicher, dass auf das Objekt innerhalb der lock
-Anweisung, in diesem Fall some_list
, immer nur von einem einzelnen Thread zugegriffen werden kann. Weitere Informationen finden Sie Ссылка .
C # hat kein Äquivalent von GIL zu Python.
Obwohl sie mit dem gleichen Problem konfrontiert sind, machen ihre Designziele sie aus anders.
Mit GIL stellt CPython sicher, dass Suchvorgänge eine Liste anhängen aus zwei Fäden ist einfach. Was auch bedeutet, dass es nur einen erlauben würde Thread kann jederzeit ausgeführt werden. Dies macht Listen und Wörterbücher threadsicher . Obwohl das macht den Job Einfacher und intuitiver macht es schwieriger, das Multithreading auszunutzen Vorteil auf Multicores.
Ohne GIL macht C # das Gegenteil. Es stellt sicher, dass die Last der Integrität auf dem Entwickler der Programm aber erlaubt es Ihnen, zu nehmen Vorteil der Ausführung mehrerer Threads gleichzeitig.
Wie einer der Diskussion -
Die GIL in CPython ist eine reine Design-Entscheidung ein großes Schloss gegen ein Schloss pro Objekt und Synchronisierung, um sicherzustellen, dass Objekte in einem kohärenten Zustand gehalten werden. Dies besteht aus einem Kompromiss - Aufgeben der vollen Macht von Multithreading.
Es ist so gewesen, dass die meisten Probleme nicht unter diesem Nachteil leiden und es gibt Bibliotheken, die Ihnen helfen, dieses Problem exklusiv zu lösen erforderlich. Das bedeutet für eine bestimmte Klasse von Problemen, die Last zu nutzen Multicore ist an den Entwickler weitergegeben, damit der Rest einfacher, intuitiver genießen kann nähern.
Hinweis: Andere Implementierungen wie IronPython haben keine GIL.
Es mag lehrreich sein, sich die Dokumentation des Java-Äquivalents anzusehen Klasse, die Sie diskutieren:
Beachten Sie, dass diese Implementierung nicht synchronisiert wird. Wenn mehrere Threads gleichzeitig auf eine
%Vor%ArrayList
-Instanz zugreifen und mindestens einer der Threads die Liste strukturell ändert, muss sein extern synchronisiert werden. (Eine Strukturmodifikation ist eine Operation, die ein oder mehrere Elemente hinzufügt oder löscht oder die Größe des Hintergrundarrays explizit ändert; das bloße Festlegen des Werts eines Elements ist keine strukturelle Modifikation.) Dies wird normalerweise durch die Synchronisierung eines Objekts erreicht, das die Klasse natürlich kapselt Liste. Wenn kein solches Objekt existiert, sollte die Liste mit demCollections.synchronizedList
Methode. Dies geschieht am besten zum Zeitpunkt der Erstellung, um einen versehentlichen unsynchronisierten Zugriff auf die Liste zu verhindern:Die von den iterator- und listIterator-Methoden dieser Klasse zurückgegebenen Iteratoren sind fail-fast : wenn die Liste zu irgendeinem Zeitpunkt nach der Erstellung des Iterators strukturell geändert wird, außer durch den eigenen iterator oder remove Methoden hinzufügen, wirft der Iterator eine
ConcurrentModificationException
. Angesichts der gleichzeitigen Modifikation versagt der Iterator daher schnell und sauber, anstatt willkürliches, nicht-deterministisches Verhalten zu einem unbestimmten Zeitpunkt in der Zukunft zu riskieren.Beachten Sie, dass das Fail-Fast-Verhalten eines Iterators nicht garantiert werden kann, da es im Allgemeinen unmöglich ist, bei unsynchronisierten simultanen Modifikationen harte Garantien zu geben. Fail-Fast-Iteratoren werfen
ConcurrentModificationException
auf Best-Effort-Basis. Daher wäre es falsch, ein Programm zu schreiben, das für seine Korrektheit von dieser Ausnahme abhängig ist: Das Fail-Fast-Verhalten von Iteratoren sollte nur zum Erkennen von Fehlern verwendet werden .
Die meisten komplexen Datenstrukturen (zum Beispiel Listen) können beschädigt werden, wenn sie ohne mehrere Threads gesperrt werden.
Da Änderungen von Referenzen atomar sind, bleibt eine Referenz immer eine gültige Referenz.
Aber es gibt ein Problem bei der Interaktion mit sicherheitskritischem Code. Daher sind alle Datenstrukturen, die von kritischem Code verwendet werden, die folgenden:
Zum Beispiel kann kritischer Code einer Liste nicht vertrauen, auf die von nicht vertrauenswürdigem Code zugegriffen werden kann. Wenn es in einer Liste übergeben wird, muss es eine private Kopie erstellen, die Vorbedingung überprüft die Kopie und dann die Kopie.
Ich werde eine wilde Vermutung darüber anstellen, was die Frage wirklich bedeutet ...
In Python werden Datenstrukturen im Interpreter beschädigt, weil Python eine Form der Referenzzählung verwendet.
Sowohl C # als auch Java verwenden Garbage Collection und tatsächlich tun eine globale Sperre, wenn sie eine vollständige Heap-Sammlung durchführen.
Daten können markiert und zwischen "Generationen" ohne Sperre verschoben werden. Aber um es wirklich zu säubern, muss alles zum Stillstand kommen. Hoffentlich ein sehr kurzer Halt, aber ein voller Stopp.
Hier ist ein interessanter Link zur CLR-Speicherbereinigung ab 2007:
Ссылка