Python-threading-Modul
PythonsthreadingModul ist eine der Standardbibliotheken, die zur Implementierung von Multithread-Programmierung verwendet werden. Multithreading ermöglicht es einem Programm, mehrere Aufgaben gleichzeitig auszuführen, wodurch die Effizienz und Reaktionsfähigkeit des Programms verbessert werden.
threadingDas Modul bietet Werkzeuge zum Erstellen und Verwalten von Threads, sodass Entwickler problemlos nebenläufige Programme schreiben können.
Was ist ein Thread?
Man kann sich Threads wie Mitarbeiter in einem Büro vorstellen:
-
Ein Single-Thread-Programm ist wie ein einziger Mitarbeiter, der alle Aufgaben nacheinander erledigen muss, wie Dokumente drucken, E-Mails beantworten und Kaffee kochen.
-
Ein Multithread-Programm ist wie mehrere Mitarbeiter, diegleichzeitigverschiedene Aufgaben ausführen können, was die Arbeitseffizienz erheblich verbessert.
In der Informatik:
- Prozess: Ein laufendes Programm mit unabhängigem Speicherbereich (zum Beispiel sind der Browser und der Musikplayer, die du gleichzeitig geöffnet hast, zwei Prozesse).
- Thread: Ein unabhängiger Ausführungsfluss innerhalb eines Prozesses und die grundlegende Einheit der CPU-Zeitplanung. Alle Threads im selben Prozessteilen sich den Speicherbereich des Prozesses(wie globale Variablen).
Warum Multithreading verwenden?
In einem Single-Thread-Programm werden Aufgaben nacheinander ausgeführt. Wenn eine Aufgabe warten muss (z. B. auf eine Netzwerkantwort oder das Lesen einer Datei), wird das gesamte Programm blockiert, bis die Aufgabe abgeschlossen ist. Multithreading ermöglicht es dem Programm, während des Wartens auf eine Aufgabe weiterhin andere Aufgaben auszuführen, wodurch die Gesamtleistung des Programms verbessert wird.
Python-Threads und der Global Interpreter Lock (GIL)
Python verfügt über einen Mechanismus namens Global Interpreter Lock (GIL), der sicherstellt, dass zu jedem Zeitpunkt nur ein Thread Python-Bytecode ausführen kann.
Was bedeutet das?Für CPU-intensive Aufgaben (wie wissenschaftliches Rechnen, Bildverarbeitung) kann Multithreading aufgrund des GIL normalerweise nicht mehrere Kerne nutzen, um die Berechnung zu beschleunigen, und kann sogar aufgrund des Thread-Wechsel-Overheads langsamer werden.
Also, wofür ist Python-Multithreading nützlich?Für I/O-intensive Aufgaben (wie Netzwerkanfragen, Dateilesen/-schreiben, Warten auf Benutzereingabe) geben Threads den GIL frei, während sie auf den Abschluss von I/O-Operationen warten, sodass andere Threads ausgeführt werden können. Dies kann die Gesamtreaktionsfähigkeit und Effizienz des Programms erheblich verbessern, denn während Sie auf die Antwort einer Webseite warten, kann das Programm eine andere Aufgabe bearbeiten.
Wie verwendet man das threading-Modul?
UmthreadingFür das Modul ist der erste Schritt, es zu importieren:
import threading import time # 用于模拟耗时操作
Die grundlegendste Methode, einen Thread zu erstellen, ist die Verwendung vonthreading.Threaddie Klasse hinzufügen.
Syntaxerklärung:
thread_obj = threading.Thread(target=函数名, args=(参数元组,))
- target: Gibt die Funktion an, die ausgeführt wird, nachdem der Thread gestartet wurde.
- args: Das an die target-Funktion übergebene Argument muss ein Tupel sein. Wenn es nur ein Argument gibt, muss es in
(parameter,)der Form `(arg,)`.
1. Threads erstellen
In Python können Sie durch Vererbung von derthreading.ThreadKlasse oder durch direkte Verwendung desthreading.ThreadKonstruktors einen Thread erstellen.
Methode 1: Vererbungthreading.ThreadKlasse
Beispiel
class MyThread(threading.Thread):
def run(self):
print("Thread beginnt mit der Ausführung")
# Schreibe hier den Code, der vom Thread ausgeführt werden soll
print("Thread-Ausführung beendet")
# Erstelle eine Thread-Instanz
thread = MyThread()
# Starte den Thread
thread.start()
# Warte, bis der Thread abgeschlossen ist
thread.join()
print("Hauptthread beendet")
Methode 2: Verwenden vonthreading.ThreadKonstruktor.
Beispiel
def my_function():
print("Thread beginnt mit der Ausführung")
# Schreibe hier den Code, der vom Thread ausgeführt werden soll
print("Thread-Ausführung beendet")
# Erstelle eine Thread-Instanz
thread = threading.Thread(target=my_function)
# Starte den Thread
thread.start()
# Warte, bis der Thread abgeschlossen ist
thread.join()
print("Hauptthread beendet")
2. Thread-Synchronisierung
In der Multithread-Programmierung können mehrere Threads gleichzeitig auf gemeinsam genutzte Ressourcen zugreifen, was zu Dateninkonsistenzproblemen führen kann. Um dies zu vermeiden, werden Thread-Synchronisierungsmechanismen wie Locks (Lock)。
Beispiel
# Lock-Objekt erstellen
lock = threading.Lock()
def my_function():
with lock:
print("Thread beginnt mit der Ausführung")
# Schreibe hier den Code, der vom Thread ausgeführt werden soll
print("Thread-Ausführung beendet")
# Erstelle eine Thread-Instanz
thread1 = threading.Thread(target=my_function)
thread2 = threading.Thread(target=my_function)
# Starte den Thread
thread1.start()
thread2.start()
# Warte, bis der Thread abgeschlossen ist
thread1.join()
thread2.join()
print("Hauptthread beendet")
3. Inter-Thread-Kommunikation
Inter-Thread-Kommunikation kann über eine Queue (Queue).Queueist threadsicher und kann Daten sicher zwischen mehreren Threads weitergeben.
Beispiel
import queue
def worker(q):
while not q.empty():
item = q.get()
print(f"Element wird verarbeitet: {item}")
q.task_done()
# Eine Queue erstellen und sie mit Daten füllen
q = queue.Queue()
for i in range(10):
q.put(i)
# Thread-Instanzen erstellen
thread1 = threading.Thread(target=worker, args=(q,))
thread2 = threading.Thread(target=worker, args=(q,))
# Die Threads starten
thread1.start()
thread2.start()
# Warten, bis alle Elemente in der Queue verarbeitet sind
q.join()
print("Alle Elemente verarbeitet")
Häufige Klassen, Methoden und Attribute
1. Kernklassen
| Klasse/Methode/Attribut | Beschreibung | Beispiel |
|---|---|---|
threading.Thread | Thread-Klasse, dient zum Erstellen und Verwalten von Threads | t = Thread(target=func, args=(1,)) |
threading.Lock | Mutex-Sperre (primitive Sperre) | lock = Lock() |
threading.RLock | Reentrierbare Sperre (kann mehrmals vom selben Thread erworben werden) | rlock = RLock() |
threading.Event | Event-Objekt, dient zur Thread-Synchronisierung | event = Event() |
threading.Condition | Bedingungsvariable, dient zur komplexen Thread-Koordination | cond = Condition() |
threading.Semaphore | Semaphore, steuert die Anzahl gleichzeitiger Threads | sem = Semaphore(3) |
threading.BoundedSemaphore | Begrenzter Semaphore (verhindert, dass der Zähler den Anfangswert überschreitet) | b_sem = BoundedSemaphore(2) |
threading.Timer | Timer-Thread, verzögerte Ausführung | timer = Timer(5.0, func) |
threading.local | Thread-lokale Daten (für jeden Thread getrennt gespeichert) | local_data = threading.local() |
2. Häufige Methoden/Attribute von Thread-Objekten
| Methode/Attribut | Beschreibung | Beispiel |
|---|---|---|
start() | Thread starten | t.start() |
run() | Methode, die vom Thread ausgeführt wird (kann überschrieben werden) | Überschreiben Sie diese Methode beim Definieren einer benutzerdefinierten Klasse |
join(timeout=None) | Blockiert den aktuellen Thread, bis der Ziel-Thread beendet ist | t.join() |
is_alive() | Prüfen, ob der Thread läuft | if t.is_alive(): |
name | Thread-Name (kann geändert werden) | t.name = "Worker-1" |
daemon | Daemon-Thread-Flag (endet automatisch, wenn der Haupt-Thread beendet wird) | t.daemon = True |
ident | Thread-Identifikator (`None` bevor der Thread gestartet wird)None) | print(t.ident) |
3. Häufige Methoden von Lock/RLock
| Methode | Beschreibung | Beispiel |
|---|---|---|
acquire(blocking=True, timeout=-1) | Sperre erwerben (blockierend oder nicht blockierend) | lock.acquire() |
release() | Sperre freigeben | lock.release() |
locked() | Prüfen, ob die Sperre gehalten wird | if not lock.locked(): |
4. Häufige Methoden von Event
| Methode | Beschreibung | Beispiel |
|---|---|---|
set() | Setzt das Event auf true und weckt alle wartenden Threads auf | event.set() |
clear() | Setzt das Event auf false zurück | event.clear() |
wait(timeout=None) | Blockiert, bis das Event true ist oder ein Timeout auftritt | event.wait(2.0) |
is_set() | Prüft den Event-Status | if event.is_set(): |
5. Häufige Methoden von Condition
| Methode | Beschreibung | Beispiel |
|---|---|---|
wait(timeout=None) | Gibt die Sperre frei und blockiert, bis benachrichtigt wird oder ein Timeout auftritt | cond.wait() |
notify(n=1) | Weckt höchstensnwartende Threads | cond.notify(2) |
notify_all() | Weckt alle wartenden Threads auf | cond.notify_all() |
6. Funktionen/Attribute auf Modulebene
| Funktion/Attribut | Beschreibung | Beispiel |
|---|---|---|
threading.active_count() | Gibt die Anzahl der aktuell aktiven Threads zurück | print(threading.active_count()) |
threading.current_thread() | Gibt das aktuelle Thread-Objekt zurück | print(threading.current_thread().name) |
threading.enumerate() | Gibt eine Liste aller aktiven Threads zurück | for t in threading.enumerate(): |
threading.main_thread() | Gibt das Haupt-Thread-Objekt zurück | if threading.current_thread() is threading.main_thread(): |
threading.get_ident() | Gibt die Kennung des aktuellen Threads zurück (Python 3.3+) | print(threading.get_ident()) |
Beispiel
1. Grundlegende Thread-Erstellung
Beispiel
def worker(num):
print(f"Worker {num} started")
threads = []
for i in range(3):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()
for t in threads:
t.join()
2. Verwenden von Sperren zum Schutz gemeinsam genutzter Ressourcen
Beispiel
count = 0
def increment():
global count
with lock: # Die Sperre automatisch erwerben und freigeben
count += 1
threads = [threading.Thread(target=increment) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
print(count) # Ausgabe: 10
3. Ereignissynchronisation
Beispiel
def waiter():
print("Warte auf Ereignis...")
event.wait()
print("Ereignis ausgelöst!")
t = threading.Thread(target=waiter)
t.start()
# Der Hauptthread löst das Ereignis aus
threading.Event().wait(2.0) # Simulierte Verzögerung
event.set()
t.join()
4. Producer-Consumer-Modell (Condition)
Beispiel
from threading import Condition
queue = []
cond = Condition()
MAX_ITEMS = 5
def producer():
for _ in range(10):
with cond:
while len(queue) >= MAX_ITEMS:
cond.wait()
item = random.randint(1, 100)
queue.append(item)
print(f"Produziert {item}")
cond.notify()
def consumer():
for _ in range(10):
with cond:
while not queue:
cond.wait()
item = queue.pop(0)
print(f"Konsumiert {item}")
cond.notify()
threading.Thread(target=producer).start()
threading.Thread(target=consumer).start()
Hinweise
Global Interpreter Lock (GIL): Pythons GIL beschränkt die Ausführung von Python-Bytecode auf jeweils einen Thread. Daher kann Multithreading bei CPU-intensiven Aufgaben möglicherweise keine Leistungsverbesserungen bieten. Bei I/O-intensiven Aufgaben ist Multithreading weiterhin von Vorteil.
Thread-Sicherheit: Stellen Sie in einer Multithread-Umgebung sicher, dass der Zugriff auf gemeinsam genutzte Ressourcen threadsicher ist, um Datenrennen und Deadlocks zu vermeiden.
Thread-Anzahl: Das Erstellen zu vieler Threads kann Systemressourcen erschöpfen und die Programmleistung beeinträchtigen. Steuern Sie die Anzahl der Threads sinnvoll, oder verwenden Sie einen Thread-Pool (
ThreadPoolExecutor) zur Verwaltung von Threads.