Multithreading in Python mit Beispiel: Lernen Sie GIL in Python

โšก Intelligente Zusammenfassung

Multithreading in Python Mehrere Threads werden innerhalb eines Prozesses ausgefรผhrt, sodass sie sich den Speicher teilen und parallel arbeiten. Das Threading-Modul erstellt und verwaltet diese Threads, wรคhrend die globale Interpretersperre (GIL) die echte Parallelitรคt einschrรคnkt. Daher eignet sich diese Technik am besten fรผr Aufgaben mit hohem Ein-/Ausgabeaufwand.

  • ๐Ÿ”˜ Thread vs. Prozess: Ein Thread ist eine Ausfรผhrungseinheit innerhalb eines Prozesses, und Threads teilen sich die Prozessdaten, wรคhrend sie einzeln ausgefรผhrt werden.
  • โ˜‘๏ธ Zwei Module: Das veraltete _thread-Modul bietet Low-Level-Aufrufe, wรคhrend das รผbergeordnete Threading-Modul der moderne Standard fรผr die Erstellung von Threads ist.
  • โœ… Threads erstellen: Erweitere die Thread-Klasse, รผberschreibe __init__ und run(), rufe dann start() zum Starten und join() zum Warten auf die Fertigstellung auf.
  • ๐Ÿงช Vermeiden Sie Rennsituationen: Eine Sperre lรคsst immer nur einen Thread gleichzeitig in einen kritischen Abschnitt eintreten und verhindert so Deadlocks und beschรคdigte gemeinsam genutzte Daten.
  • ๏ธ Das GIL verstehen: Die globale Interpretersperre (GIL) erlaubt es einem Thread, gleichzeitig zu laufen. Python Bytecode auf einmal verarbeiten, daher ist fรผr rechenintensive Aufgaben Multiprocessing erforderlich.
  • ๐Ÿค– KI-Workloads: Multithreading beschleunigt eingabe-/ausgabegebundene KI-Schritte wie das Laden von Daten, wรคhrend NumPy und PyTorch gibt den GIL fรผr parallele Berechnungen frei.

Multithreading in Python

Das Python Die Programmiersprache ermรถglicht die Nutzung von Multiprocessing oder Multithreading. In diesem Tutorial lernen Sie, wie man Multithread-Anwendungen schreibt. Python.

Was ist ein Faden?

Ein Thread ist eine Ausfรผhrungseinheit in der nebenlรคufigen Programmierung. Multithreading ist eine Technik, die es einer CPU ermรถglicht, mehrere Aufgaben eines Prozesses gleichzeitig auszufรผhren. Diese Threads kรถnnen unabhรคngig voneinander ausgefรผhrt werden und teilen sich dabei die Prozessressourcen.

Was ist ein Prozess?

Ein Prozess ist im Grunde ein Programm, das gerade ausgefรผhrt wird. Wenn Sie eine Anwendung auf Ihrem Computer starten (z. B. einen Browser oder einen Texteditor), erstellt das Betriebssystem einen solchen Prozess.

Was ist Multithreading in Python?

Multithreading in Python Multithreading ist eine bekannte Programmiertechnik, bei der mehrere Threads innerhalb eines Prozesses ihren Datenspeicher mit dem Hauptthread teilen. Dies ermรถglicht eine einfache und effiziente Informationsweitergabe und Kommunikation zwischen den Threads. Threads sind ressourcenschonender als Prozesse. Mehrere Threads kรถnnen unabhรคngig voneinander ausgefรผhrt werden und teilen sich dabei die Prozessressourcen. Ziel des Multithreadings ist es, mehrere Aufgaben und Funktionen gleichzeitig auszufรผhren.

Was ist Multiprocessing?

Multiprozessing ermรถglicht Ihnen, mehrere unabhรคngige Prozesse gleichzeitig auszufรผhren. Diese Prozesse teilen ihre Ressourcen nicht und kommunizieren รผber IPC.

Python Multithreading vs. Multiprocessing

Um Prozesse und Threads zu verstehen, betrachten Sie folgendes Szenario: Eine .exe-Datei auf Ihrem Computer ist ein Programm. Wenn Sie sie รถffnen, lรคdt das Betriebssystem sie in den Arbeitsspeicher, und die CPU fรผhrt sie aus. Die Instanz des Programms, die nun ausgefรผhrt wird, wird als Prozess bezeichnet.

Jeder Prozess besteht aus zwei grundlegenden Komponenten:

  • Der Code des Chamรคleons
  • Die Fakten

Nun kann ein Prozess einen oder mehrere aufgerufene Unterteile enthalten Threads. Dies hรคngt von der Architektur des Betriebssystems ab. Man kann sich einen Thread als einen Teil eines Prozesses vorstellen, der vom Betriebssystem separat ausgefรผhrt werden kann.

Anders ausgedrรผckt handelt es sich um einen Strom von Anweisungen, die vom Betriebssystem unabhรคngig ausgefรผhrt werden kรถnnen. Threads innerhalb eines einzelnen Prozesses teilen sich die Daten dieses Prozesses und sind so konzipiert, dass sie zusammenarbeiten, um Parallelverarbeitung zu ermรถglichen.

Warum Multithreading verwenden?

Mit Multithreading kรถnnen Sie eine Anwendung in mehrere Unteraufgaben aufteilen und diese Aufgaben gleichzeitig ausfรผhren. Wenn Sie Multithreading richtig verwenden, kรถnnen Sie die Geschwindigkeit, Leistung und Darstellung Ihrer Anwendung verbessern.

Python Multithreading

Python Unterstรผtzt Konstrukte fรผr Multiprocessing und Multithreading. In diesem Tutorial konzentrieren Sie sich hauptsรคchlich auf die Implementierung. Multithreading Anwendungen mit PythonEs gibt zwei Hauptmodule, die zur Verarbeitung von Threads verwendet werden kรถnnen. Python:

  1. Das Faden Modul und
  2. Das einfรคdeln Modulen

Jedoch in PythonEs gibt auch etwas, das man Global Interpreter Lock (GIL) nennt. Es ermรถglicht keine wesentlichen Leistungssteigerungen und kann sogar Veteran die Leistung einiger Multithread-Anwendungen. Alles darรผber erfahren Sie in den nรคchsten Abschnitten dieses Tutorials.

Die Thread- und Threading-Module

Die beiden Module, die Sie in diesem Tutorial kennenlernen werden, sind: Thread-Modul und der Threading-Modul.

Das Thread-Modul ist jedoch schon lange veraltet. Beginnend mit Python 3, es wurde als veraltet gekennzeichnet und ist nur zugรคnglich als _Faden fรผr Abwรคrtskompatibilitรคt.

Sie sollten die hรถhere Ebene verwenden einfรคdeln Modul fรผr Anwendungen, die Sie bereitstellen mรถchten. Das Thread-Modul wurde hier nur zu Schulungszwecken behandelt.

Das Thread-Modul

Die Syntax zum Erstellen eines neuen Threads mit diesem Modul lautet wie folgt:

thread.start_new_thread(function_name, arguments)

Okay, jetzt haben Sie die grundlegende Theorie behandelt, um mit dem Codieren zu beginnen. Also, รถffnen Sie Ihr IDLE oder einen Notizblock und geben Sie Folgendes ein:

import time
import _thread

def thread_test(name, wait):
   i = 0
   while i <= 3:
      time.sleep(wait)
      print("Running %s\n" %name)
      i = i + 1

   print("%s has finished execution" %name)

if __name__ == "__main__":
    
    _thread.start_new_thread(thread_test, ("First Thread", 1))
    _thread.start_new_thread(thread_test, ("Second Thread", 2))
    _thread.start_new_thread(thread_test, ("Third Thread", 3))

Speichern Sie die Datei und drรผcken Sie F5, um das Programm auszufรผhren. Wenn alles richtig gemacht wurde, sollten Sie folgende Ausgabe sehen:

Das Thread-Modul

In den folgenden Abschnitten erfahren Sie mehr รผber Rennbedingungen und wie Sie damit umgehen kรถnnen.

Das Thread-Modul

CODE-ERKLร„RUNG

  1. Diese Anweisungen importieren das Zeit- und Thread-Modul, das zur Steuerung der Ausfรผhrung und Verzรถgerung von Prozessen verwendet wird. Python Threads.
  2. Hier haben Sie eine Funktion namens definiert thread_test, welches von der aufgerufen wird start_new_thread Die Funktion fรผhrt eine while-Schleife viermal aus und gibt den Namen des aufrufenden Threads aus. Nach Abschluss der Schleife wird eine Meldung ausgegeben, die besagt, dass der Thread die Ausfรผhrung beendet hat.
  3. Dies ist der Hauptabschnitt Ihres Programms. Hier rufen Sie einfach die an start_new_thread Methode mit der thread_test Sie kรถnnen die Funktion als Argument รผbergeben. Dadurch wird ein neuer Thread fรผr die รผbergebene Funktion erstellt und deren Ausfรผhrung gestartet. Beachten Sie, dass Sie `thread_test` durch eine beliebige andere Funktion ersetzen kรถnnen, die Sie als Thread ausfรผhren mรถchten.

Das Threading-Modul

Dieses Modul ist die High-Level-Implementierung von Threads in Python und ist der De-facto-Standard fรผr die Verwaltung von Multithread-Anwendungen. Es bietet im Vergleich zum Thread-Modul eine Vielzahl von Funktionen.

Struktur des Threading-Moduls

Struktur des Threading-Moduls

Hier ist eine Liste einiger nรผtzlicher Funktionen, die in diesem Modul definiert sind:

Funktionsname Beschreibung
activeCount() Gibt die Anzahl von zurรผck Thread Objekte, die noch leben.
aktuellerThread() Gibt das aktuelle Objekt der Thread-Klasse zurรผck.
aufzรคhlen() Listet alle aktiven Thread-Objekte auf.
isDaemon() Gibt true zurรผck, wenn der Thread ein Daemon ist.
ist am Leben() Gibt true zurรผck, wenn der Thread noch aktiv ist.
Methoden der Thread-Klasse
Start() Startet die Aktivitรคt eines Threads. Es darf fรผr jeden Thread nur einmal aufgerufen werden, da es bei mehrmaligem Aufruf einen Laufzeitfehler auslรถst.
Lauf() Diese Methode bezeichnet die Aktivitรคt eines Threads und kann von einer Klasse รผberschrieben werden, die die Thread-Klasse erweitert.
beitreten() Es blockiert die Ausfรผhrung anderen Codes, bis der Thread, fรผr den die Methode join() aufgerufen wurde, beendet wird.

Hintergrundgeschichte: Die Thread-Klasse

Bevor Sie mit der Programmierung von Multithread-Programmen mithilfe des Threading-Moduls beginnen, ist es unerlรคsslich, die Thread-Klasse zu verstehen. Die Thread-Klasse ist die primรคre Klasse, die die Vorlage und die Operationen eines Threads definiert. Python.

Die gebrรคuchlichste Methode zur Erstellung eines Multithread-Systems Python Die Anwendung besteht darin, eine Klasse zu deklarieren, die die Klasse Thread erweitert und deren run()-Methode รผberschreibt.

Zusammenfassend stellt die Thread-Klasse eine Codesequenz dar, die separat ausgefรผhrt wird Faden der Kontrolle.

Wenn Sie eine Multithread-App schreiben, gehen Sie also wie folgt vor:

  1. Definiere eine Klasse, die die Thread-Klasse erweitert.
  2. รœberschreiben Sie die __init__ Konstruktor
  3. รœberschreiben Sie die Lauf() Methode

Sobald ein Thread-Objekt erstellt wurde, wird das Start() Mit dieser Methode kann die Ausfรผhrung dieser Aktivitรคt gestartet werden, und die beitreten() Die Methode kann verwendet werden, um den gesamten anderen Code zu blockieren, bis die aktuelle Aktivitรคt beendet ist.

Nun versuchen wir, Ihr vorheriges Beispiel mithilfe des Threading-Moduls umzusetzen. Starten Sie erneut Ihr Programm. IDLE und geben Sie Folgendes ein:

import time
import threading

class threadtester (threading.Thread):
    def __init__(self, id, name, i):
       threading.Thread.__init__(self)
       self.id = id
       self.name = name
       self.i = i
       
    def run(self):
       thread_test(self.name, self.i, 5)
       print ("%s has finished execution " %self.name)

def thread_test(name, wait, i):

    while i:
       time.sleep(wait)
       print ("Running %s \n" %name)
       i = i - 1

if __name__=="__main__":
    thread1 = threadtester(1, "First Thread", 1)
    thread2 = threadtester(2, "Second Thread", 2)
    thread3 = threadtester(3, "Third Thread", 3)

    thread1.start()
    thread2.start()
    thread3.start()

    thread1.join()
    thread2.join()
    thread3.join()

Dies wird die Ausgabe sein, wenn Sie den obigen Code ausfรผhren:

Hintergrundgeschichte: Die Thread-Klasse

CODE-ERKLร„RUNG

Hintergrundgeschichte: Die Thread-Klasse

  1. Dieser Abschnitt entspricht unserem vorherigen Beispiel. Hier importieren Sie das Modul `time` und `thread`, die zur Steuerung der Ausfรผhrung und der Verzรถgerungen verwendet werden. Python Threads.
  2. In diesem Teil erstellen Sie eine Klasse namens Threadtester, die die Klasse erbt oder erweitert Thread Klasse des Threading-Moduls. Dies ist eine der gebrรคuchlichsten Methoden, um Threads zu erstellen in PythonSie sollten jedoch nur den Konstruktor und die Lauf() Methode in Ihrer App. Wie Sie im obigen Codebeispiel sehen kรถnnen, ist die __init__ Methode (Konstruktor) wurde รผberschrieben. Ebenso haben Sie auch das รผberschrieben Lauf() Methode. Es enthรคlt den Code, den Sie innerhalb eines Threads ausfรผhren mรถchten. In diesem Beispiel haben Sie die Funktion thread_test() aufgerufen.
  3. Dies ist die Methode thread_test(), die den Wert von i Als Argument wird i in jeder Iteration um 1 verringert, und der restliche Code wird so lange ausgefรผhrt, bis i gleich 0 wird. In jeder Iteration wird der Name des aktuell ausgefรผhrten Threads ausgegeben und fรผr die angegebene Anzahl von Sekunden gewartet (die ebenfalls als Argument รผbergeben wird).
  4. thread1 = threadtester(1, โ€žFirst Threadโ€œ, 1) Hier erstellen wir einen Thread und รผbergeben die drei Parameter, die wir in __init__ deklariert haben. Der erste Parameter ist die ID des Threads, der zweite Parameter ist der Name des Threads und der dritte Parameter ist der Zรคhler, der bestimmt, wie oft die while-Schleife ausgefรผhrt werden soll.
  5. thread2.start() Die Methode start() dient zum Starten der Ausfรผhrung eines Threads. Intern ruft die Funktion start() die Methode run() Ihrer Klasse auf.
  6. thread3.join() Die Methode join() blockiert die Ausfรผhrung anderen Codes und wartet, bis der Thread, in dem sie aufgerufen wurde, beendet ist.

Wie Sie bereits wissen, haben die Threads innerhalb desselben Prozesses Zugriff auf den Speicher und die Daten dieses Prozesses. Wenn also mehrere Threads gleichzeitig versuchen, die Daten zu รคndern oder darauf zuzugreifen, kรถnnen Fehler auftreten.

Im nรคchsten Abschnitt sehen Sie die verschiedenen Arten von Komplikationen, die auftreten kรถnnen, wenn Threads auf Daten und den kritischen Abschnitt zugreifen, ohne vorher auf bestehende Zugriffstransaktionen zu prรผfen.

Deadlocks und Race Conditions

Bevor wir uns mit Deadlocks und Race Conditions befassen, ist es hilfreich, einige grundlegende Definitionen im Zusammenhang mit der nebenlรคufigen Programmierung zu verstehen:

  • Kritischer Abschnitt: Es handelt sich um ein Codefragment, das auf gemeinsam genutzte Variablen zugreift oder diese verรคndert und als atomare Transaktion ausgefรผhrt werden muss.
  • Kontextwechsel: Es handelt sich um den Prozess, den eine CPU durchfรผhrt, um den Zustand eines Threads zu speichern, bevor sie von einer Aufgabe zur anderen wechselt, damit er spรคter an derselben Stelle fortgesetzt werden kann.

Blockaden

Blockaden sind das am meisten gefรผrchtete Problem, mit dem Entwickler beim Schreiben von nebenlรคufigen/multithreadfรคhigen Anwendungen konfrontiert werden. PythonAm besten lassen sich Deadlocks anhand des klassischen Informatikbeispiels verstehen, das als Deadlock bekannt ist. Restaurants PhiloSophers Problem.

Die Problemstellung fรผr speisende Philosophen lautet wie folgt:

Fรผnf Philosophen sitzen an einem runden Tisch mit fรผnf Tellern Spaghetti (einer Nudelsorte) und fรผnf Gabeln, wie in der Abbildung dargestellt.

Restaurants PhiloSophers Problem

Restaurants PhiloSophers Problem

Ein Philosoph muss zu jeder Zeit entweder essen oder nachdenken.

Darรผber hinaus muss ein Philosoph die beiden Gabeln neben ihm nehmen (also die linke und die rechte Gabel), bevor er die Spaghetti essen kann. Das Deadlock-Problem tritt auf, wenn alle fรผnf Philosophen gleichzeitig ihre rechte Gabel nehmen.

Da jeder der Philosophen eine Gabel hat, werden sie alle warten, bis die anderen ihre Gabeln hinlegen. Infolgedessen wird keiner von ihnen Spaghetti essen kรถnnen.

ร„hnlich verhรคlt es sich in einem parallelen System: Ein Deadlock tritt auf, wenn verschiedene Threads oder Prozesse (Philosophen) gleichzeitig versuchen, die gemeinsam genutzten Systemressourcen (Forks) zu beanspruchen. Dies fรผhrt dazu, dass keiner der Prozesse ausgefรผhrt werden kann, da sie auf eine andere Ressource warten, die von einem anderen Prozess gehalten wird.

Rennbedingungen

Eine Race Condition ist ein unerwรผnschter Programmzustand, der auftritt, wenn ein System zwei oder mehr Operationen gleichzeitig ausfรผhrt. Betrachten wir beispielsweise diese einfache for-Schleife:

i=0; # a global variable
for x in range(100):
    print(i)
    i+=1;

Wenn Sie erstellen n Bei der Anzahl der Threads, die diesen Code gleichzeitig ausfรผhren, lรคsst sich der Wert von `i` (der von den Threads gemeinsam genutzt wird) nach Programmende nicht bestimmen. Dies liegt daran, dass sich Threads in einer realen Multithreading-Umgebung รผberschneiden kรถnnen und der von einem Thread abgerufene und geรคnderte Wert von `i` sich รคndern kann, wรคhrend ein anderer Thread darauf zugreift.

Dies sind die beiden Hauptklassen von Problemen, die in einem Multithread- oder verteilten System auftreten kรถnnen. Python Anwendung. Im nรคchsten Abschnitt erfahren Sie, wie Sie dieses Problem durch die Synchronisierung von Threads beheben kรถnnen.

Syncchronisierende Threads

Um mit Race Conditions, Deadlocks und anderen Thread-basierten Problemen umzugehen, bietet das Threading-Modul die Zahnscheiben Objekt. Die Idee ist, dass ein Thread, wenn er auf eine bestimmte Ressource zugreifen mรถchte, eine Sperre fรผr diese Ressource erhรคlt. Sobald ein Thread eine bestimmte Ressource sperrt, kann kein anderer Thread darauf zugreifen, bis die Sperre aufgehoben wird. Dadurch werden die ร„nderungen an der Ressource atomar und Race Conditions werden vermieden.

Eine Sperre ist ein Synchronisierungsprimitiv auf niedriger Ebene, das von der _Faden Modul. Ein Schloss kann sich zu jedem Zeitpunkt in einem von zwei Zustรคnden befinden: verschlossen or entsperrt. Es unterstรผtzt zwei Methoden:

  1. erwerben(): Wenn der Sperrzustand aufgehoben ist, รคndert der Aufruf der Methode `acquire()` den Zustand in gesperrt und gibt die Funktion zurรผck. Befindet sich der Zustand jedoch im gesperrten Zustand, wird der Aufruf von `acquire()` blockiert, bis die Methode `release()` von einem anderen Thread aufgerufen wird.
  2. freigeben(): Mit der Methode release() wird der Zustand auf โ€žunlockedโ€œ gesetzt, also eine Sperre aufgehoben. Es kann von jedem Thread aufgerufen werden, nicht unbedingt von dem Thread, der die Sperre erhalten hat.

Hier ist ein Beispiel fรผr die Verwendung von Sperren in Ihren Apps. Starten Sie Ihre IDLE und geben Sie Folgendes ein:

import threading
lock = threading.Lock()

def first_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the first funcion')
        lock.release()

def second_function():
    for i in range(5):
        lock.acquire()
        print ('lock acquired')
        print ('Executing the second funcion')
        lock.release()

if __name__=="__main__":
    thread_one = threading.Thread(target=first_function)
    thread_two = threading.Thread(target=second_function)

    thread_one.start()
    thread_two.start()

    thread_one.join()
    thread_two.join()

Drรผcken Sie nun F5. Sie sollten eine Ausgabe wie diese sehen:

Syncchronisierende Threads

CODE-ERKLร„RUNG

Syncchronisierende Threads

  1. Hier erstellen Sie einfach eine neue Sperre, indem Sie die aufrufen threading.Lock() Werksfunktion. Intern gibt Lock() eine Instanz der effektivsten konkreten Lock-Klasse zurรผck, die von der Plattform verwaltet wird.
  2. In der ersten Anweisung erhalten Sie die Sperre, indem Sie die Methode acquire() aufrufen. Wenn die Sperre erteilt wurde, drucken Sie โ€žSperre erworbenโ€œ zur Konsole. Sobald die Ausfรผhrung des gesamten Codes, den der Thread ausfรผhren soll, abgeschlossen ist, heben Sie die Sperre auf, indem Sie die Methode release() aufrufen.

Die Theorie ist gut und schรถn, aber woher wissen Sie, dass die Sperre tatsรคchlich funktioniert hat? Wenn Sie sich die Ausgabe ansehen, werden Sie feststellen, dass jede print-Anweisung genau eine Zeile nach der anderen ausgibt. Erinnern Sie sich, dass in einem frรผheren Beispiel die Ausgaben von print ungeordnet waren, da mehrere Threads gleichzeitig auf die print()-Methode zugegriffen haben. Hier wird die print-Funktion erst aufgerufen, nachdem die Sperre erworben wurde. Daher werden die Ausgaben nacheinander und zeilenweise angezeigt.

Abgesehen von Schlรถssern, Python Unterstรผtzt auรŸerdem einige andere Mechanismen zur Handhabung der Thread-Synchronisierung, wie unten aufgefรผhrt:

  1. RLocks
  2. Semaphores
  3. Erkrankungen
  4. Veranstaltungen und
  5. Barriers

Global Interpreter Lock (und wie man damit umgeht)

Bevor wir auf die Einzelheiten eingehen PythonLassen Sie uns, GIL, einige Begriffe definieren, die zum Verstรคndnis des folgenden Abschnitts hilfreich sein werden:

  1. CPU-gebundener Code: Dies bezieht sich auf jeden Codeabschnitt, der direkt von der CPU ausgefรผhrt wird.
  2. E/A-gebundener Code: Dies kann jeder Code sein, der รผber das Betriebssystem auf das Dateisystem zugreift.
  3. CPython: es ist die Referenz Implementierung of Python und kann als der in C geschriebene Interpreter beschrieben werden und Python (Programmiersprache).

Was ist GIL in Python?

Globale Dolmetschersperre (GIL) in Python Der GIL (Global Interpreter Lock) ist eine Prozesssperre oder ein Mutex, der bei der Prozessverwaltung verwendet wird. Er stellt sicher, dass jeweils nur ein Thread auf eine bestimmte Ressource zugreifen kann und verhindert die gleichzeitige Verwendung von Objekten und Bytecode. Dies fรผhrt zu einer Leistungssteigerung bei Single-Thread-Programmen. Python ist sehr einfach und leicht umzusetzen.

Mit einer Sperre kann sichergestellt werden, dass zu einem bestimmten Zeitpunkt nur ein Thread Zugriff auf eine bestimmte Ressource hat.

Eines der Merkmale von Python Das liegt daran, dass es eine globale Sperre fรผr jeden Interpreterprozess verwendet, was bedeutet, dass jeder Prozess die Python Der Interpreter selbst als Ressource.

Nehmen wir beispielsweise an, Sie haben Folgendes geschrieben: Python Ein Programm, das zwei Threads fรผr CPU- und E/A-Operationen nutzt. Folgendes passiert bei der Ausfรผhrung dieses Programms:

  1. Das Python Der Interpreter erstellt einen neuen Prozess und erzeugt die Threads.
  2. Wenn Thread-1 mit der Ausfรผhrung beginnt, erfasst er zunรคchst die GIL und sperrt sie.
  3. Wenn Thread-2 jetzt ausgefรผhrt werden mรถchte, muss er auf die Freigabe der GIL warten, auch wenn ein anderer Prozessor frei ist.
  4. Nehmen wir nun an, dass Thread 1 auf eine E/A-Operation wartet. Zu diesem Zeitpunkt gibt er den GIL frei und Thread 2 holt ihn sich.
  5. Wenn Thread-1 nach Abschluss der E/A-Vorgรคnge jetzt ausgefรผhrt werden mรถchte, muss er erneut auf die Freigabe der GIL durch Thread-2 warten.

Aus diesem Grund kann jeweils nur ein Thread auf den Interpreter zugreifen, was bedeutet, dass immer nur ein Thread ausgefรผhrt wird. Python Code zu einem bestimmten Zeitpunkt.

Bei einem Einkernprozessor ist dies unproblematisch, da die Threads mithilfe von Time Slicing (siehe erster Abschnitt dieses Tutorials) verwaltet werden. Bei Mehrkernprozessoren hingegen beeintrรคchtigt eine rechenintensive Funktion, die auf mehreren Threads ausgefรผhrt wird, die Effizienz des Programms erheblich, da nicht alle verfรผgbaren Kerne gleichzeitig genutzt werden.

Warum wurde GIL benรถtigt?

Die CPython Der Garbage Collector verwendet eine effiziente Speicherverwaltungstechnik, die als Referenzzรคhlung bekannt ist. So funktioniert sie: Jedes Objekt im Speicher Python Jedes Objekt besitzt einen Referenzzรคhler, der erhรถht wird, wenn es einer neuen Variablen zugewiesen oder einem Container (wie Tupeln, Listen usw.) hinzugefรผgt wird. Umgekehrt verringert sich der Referenzzรคhler, wenn die Referenz ihren Gรผltigkeitsbereich verlรคsst oder die `del`-Anweisung aufgerufen wird. Erreicht der Referenzzรคhler eines Objekts den Wert 0, wird es vom Garbage Collector entfernt und der belegte Speicher freigegeben.

Das Problem besteht jedoch darin, dass die Referenzzรคhlervariable wie jede andere globale Variable anfรคllig fรผr Race Conditions ist. Um dieses Problem zu lรถsen, haben die Entwickler von Python Wir entschieden uns fรผr die Verwendung der globalen Interpretersperre. Die Alternative wรคre gewesen, jedem Objekt eine Sperre hinzuzufรผgen, was zu Deadlocks und einem erhรถhten Aufwand durch acquire()- und release()-Aufrufe gefรผhrt hรคtte.

Daher stellt GIL eine erhebliche Einschrรคnkung fรผr Multithreading dar. Python Programme, die rechenintensive Operationen ausfรผhren (und dadurch effektiv Single-Threaded sind). Wenn Sie mehrere CPU-Kerne in Ihrer Anwendung nutzen mรถchten, verwenden Sie die Mehrfachverarbeitung Modul statt.

Hรคufig gestellte Fragen

Verwenden Sie Threading fรผr E/A-intensive Aufgaben wie Netzwerkaufrufe, Dateizugriffe oder Datenbankabfragen, bei denen Threads auf externe Ressourcen warten. Nutzen Sie Multiprocessing fรผr CPU-intensive Aufgaben wie rechenintensive Vorgรคnge, da es auf mehreren Kernen ausgefรผhrt wird und den GIL umgeht.

Nicht standardmรครŸig. PEP 703 fรผhrte eine optionale, freithreadfรคhige Version ein. Python 3.13, das den GIL deaktiviert, und Python Version 3.14 setzt dies fort. Standard-Builds enthalten weiterhin den GIL, sodass die meisten Programme weiterhin genau wie zuvor laufen.

Ein Daemon-Thread lรคuft im Hintergrund und blockiert das Beenden des Programms nicht. Wenn nur noch Daemon-Threads vorhanden sind, Python SchlieรŸt sie und beendet das Programm. Setzen Sie einen Thread mit `thread.daemon = True`, bevor Sie `start()` aufrufen.

Die Klasse `Thread` gibt das Ergebnis von `run()` nicht direkt zurรผck. Speichern Sie den Wert im Thread-Objekt oder einer gemeinsam genutzten Warteschlange oder verwenden Sie `concurrent.futures.ThreadPoolExecutor`, dessen `submit()`-Methode ein Future-Objekt zurรผckgibt, das Sie mit `result()` auslesen kรถnnen.

ThreadPoolExecutor ist eine Hilfsfunktion auf hoher Ebene, die einen Pool von Worker-Threads fรผr Sie verwaltet. Sie รผbermitteln Aufgaben mit submit() oder map(), und die Funktion kรผmmert sich um die Erstellung, Wiederverwendung und Bereinigung der Threads und gibt Future-Objekte zurรผck, die jeweils ein Ergebnis enthalten.

Sie kรถnnen Hunderte oder Tausende von Threads erstellen, aber der GIL lรคsst nur einen einzigen Thread ausfรผhren. Python Bytecode zu jedem Zeitpunkt. Sie รผberlappen sich auch wรคhrend E/A-Wartezeiten, daher helfen viele parallele Threads E/A-intensiven Programmen weitaus mehr als CPU-intensiven.

Ja, hauptsรคchlich fรผr E/A-intensive Schritte wie das Laden von Daten, das Aufrufen von APIs oder das Lesen von Dateien. Aufwรคndiges Training ist CPU-lastig, daher werden Bibliotheken wie NumPy und Py verwendet.TorEntweder man gibt den GIL im C-Code frei oder man setzt auf Multiprocessing fรผr echten Parallelismus.

Ja. GitHub Copilot vervollstรคndigt hรคufige Muster wie Thread-Unterklassen, die Verwendung von Sperren und die Einrichtung von ThreadPoolExecutor anhand eines Kommentars oder Funktionsnamens. รœberprรผfen Sie den generierten Code immer auf Race Conditions und korrekte Sperrbehandlung, bevor Sie ihn verwenden.

Fassen Sie diesen Beitrag mit folgenden Worten zusammen: