Ich glaube, es ist eine dumme Frage, aber ich kann es immer noch nicht finden. Eigentlich ist es besser, es in zwei Fragen zu trennen:
1) Habe ich Recht, dass wir viele Threads haben könnten, aber wegen GIL in einem Moment wird nur ein Thread ausgeführt?
2) Wenn ja, warum brauchen wir noch Schlösser? Wir verwenden Sperren, um den Fall zu vermeiden, wenn zwei Threads versuchen, ein gemeinsames Objekt zu lesen / schreiben, weil GIL twi-Threads nicht in einem Moment ausgeführt werden können, oder?
GIL schützt die Python Interals. Das heißt:
Aber GIL schützt deinen eigenen Code nicht. Zum Beispiel, wenn Sie diesen Code haben:
%Vor% Das wird den Wert von self.some_number
lesen, some_number+1
berechnen und dann wieder in self.some_number
schreiben.
Wenn Sie das in zwei Threads tun, können die Operationen (lesen, hinzufügen, schreiben) eines Threads und des anderen gemischt werden, so dass das Ergebnis falsch ist.
Dies könnte die Reihenfolge der Ausführung sein:
self.some_number
(0) self.some_number
(0) some_number+1
(1) some_number+1
(1) self.some_number
self.some_number
Sie verwenden Sperren, um diese Reihenfolge der Ausführung zu erzwingen:
self.some_number
(0) some_number+1
(1) self.some_number
self.some_number
(1) some_number+1
(2) self.some_number
Es gibt zwei Funktionen, die Inkremente implementieren. Einer verwendet Sperren und der andere nicht.
Die Funktion increment_in_x_threads
implementiert die parallele Ausführung der Inkrementierungsfunktion in vielen Threads.
Wenn Sie dies jetzt mit einer ausreichend großen Anzahl von Threads ausführen, wird fast sicher ein Fehler auftreten:
%Vor%In meinem Fall hat es gedruckt:
%Vor%Also ohne Sperren gab es viele Fehler (33% der Inkremente fehlgeschlagen). Auf der anderen Seite, mit Sperren war es 20 mal langsamer.
Natürlich sind beide Zahlen aufgebläht, weil ich 70 Threads benutzt habe, aber das zeigt die allgemeine Idee.
Zu jedem Zeitpunkt, ja, nur ein Thread führt Python-Code aus (andere Threads können einige IO, NumPy, was auch immer ausführen). Das ist meistens richtig. Dies ist jedoch trivialerweise bei jedem Einzelprozessorsystem der Fall, und dennoch benötigen Benutzer immer noch Sperren für Einzelprozessorsysteme.
Sehen Sie sich den folgenden Code an:
%Vor% Mit einem Thread ist alles in Ordnung. Bei zwei Threads erhalten Sie möglicherweise eine Ausnahme von queue.pop()
, weil der andere Thread zuerst queue.pop()
für das letzte Element genannt hat. Also müsstest du irgendwie damit umgehen. Die Verwendung einer Sperre ist eine einfache Lösung. Sie können auch eine ordnungsgemäße gleichzeitige Warteschlange verwenden (wie im Modul queue
). Wenn Sie jedoch in das Modul queue
schauen, werden Sie feststellen, dass das Objekt Queue
ein threading.Lock()
enthält. So oder so Sie Sperren verwenden.
Es ist ein häufiger Anfängerfehler, Multithreading-Code ohne die erforderlichen Sperren zu schreiben. Sie schauen sich Code an und denken, "das wird gut funktionieren" und dann viele Stunden später herausfinden, dass etwas wirklich Bizarres passiert ist, weil die Threads nicht richtig synchronisiert wurden.
Kurz gesagt, gibt es in einem Multithread-Programm viele Stellen, an denen Sie verhindern müssen, dass ein anderer Thread eine Struktur ändert, bis Sie einige Änderungen vorgenommen haben. Auf diese Weise können Sie die Invarianten Ihrer Daten beibehalten. Wenn Sie keine Invarianten pflegen können, ist es im Grunde unmöglich, einen korrekten Code zu schreiben.
Oder geben Sie den kürzesten Weg ein: "Sie brauchen keine Sperren, wenn es Ihnen egal ist, ob Ihr Code korrekt ist."
Die GIL verhindert die gleichzeitige Ausführung mehrerer Threads, aber nicht in allen Situationen.
Die GIL wird vorübergehend während der von Threads ausgeführten E / A-Operationen freigegeben. Das bedeutet, dass mehrere Threads gleichzeitig ausgeführt werden können. Das ist ein Grund, warum Sie immer noch Sperren benötigen.
Ich weiß nicht, wo ich diese Referenz gefunden habe ... in einem Video oder so - schwer zu lesen, aber Sie können selbst weiter nachforschen
Tags und Links python multithreading