Was ist C # 's Version der GIL?

8

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.

    
Thanatos 26.10.2010, 17:29
quelle

6 Antworten

11

Die meisten anderen Sprachen, die Threading unterstützen, haben kein Äquivalent zur Python-GIL. Sie müssen Mutexe verwenden, entweder implizit oder explizit.

    
Ignacio Vazquez-Abrams 26.10.2010 17:33
quelle
4

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 Ссылка .

    
Pieter van Ginkel 26.10.2010 17:32
quelle
3

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.

    
pyfunc 26.10.2010 17:54
quelle
1

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 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 dem Collections.synchronizedList Methode. Dies geschieht am besten zum Zeitpunkt der Erstellung, um einen versehentlichen unsynchronisierten Zugriff auf die Liste zu verhindern:

%Vor%      

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 .

    
Daniel Pryden 26.10.2010 17:53
quelle
0

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:

  • Von nicht vertrauenswürdigem Code nicht zugreifbar und von vertrauenswürdigem Code
  • korrekt gesperrt / verwendet
  • Unveränderlich (String-Klasse)
  • Vor der Verwendung kopiert (Werttypparameter)
  • Wird in vertrauenswürdigem Code geschrieben und verwendet internes Sperren, um einen sicheren Status zu gewährleisten

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.

    
CodesInChaos 26.10.2010 18:01
quelle
0

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:
Ссылка

    
Zan Lynx 26.10.2010 18:56
quelle

Tags und Links