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

import threading

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

import threading

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

import threading

# 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 threading
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/AttributBeschreibungBeispiel
threading.ThreadThread-Klasse, dient zum Erstellen und Verwalten von Threadst = Thread(target=func, args=(1,))
threading.LockMutex-Sperre (primitive Sperre)lock = Lock()
threading.RLockReentrierbare Sperre (kann mehrmals vom selben Thread erworben werden)rlock = RLock()
threading.EventEvent-Objekt, dient zur Thread-Synchronisierungevent = Event()
threading.ConditionBedingungsvariable, dient zur komplexen Thread-Koordinationcond = Condition()
threading.SemaphoreSemaphore, steuert die Anzahl gleichzeitiger Threadssem = Semaphore(3)
threading.BoundedSemaphoreBegrenzter Semaphore (verhindert, dass der Zähler den Anfangswert überschreitet)b_sem = BoundedSemaphore(2)
threading.TimerTimer-Thread, verzögerte Ausführungtimer = Timer(5.0, func)
threading.localThread-lokale Daten (für jeden Thread getrennt gespeichert)local_data = threading.local()

2. Häufige Methoden/Attribute von Thread-Objekten

Methode/AttributBeschreibungBeispiel
start()Thread startent.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 istt.join()
is_alive()Prüfen, ob der Thread läuftif t.is_alive():
nameThread-Name (kann geändert werden)t.name = "Worker-1"
daemonDaemon-Thread-Flag (endet automatisch, wenn der Haupt-Thread beendet wird)t.daemon = True
identThread-Identifikator (`None` bevor der Thread gestartet wird)None)print(t.ident)

3. Häufige Methoden von Lock/RLock

MethodeBeschreibungBeispiel
acquire(blocking=True, timeout=-1)Sperre erwerben (blockierend oder nicht blockierend)lock.acquire()
release()Sperre freigebenlock.release()
locked()Prüfen, ob die Sperre gehalten wirdif not lock.locked():

4. Häufige Methoden von Event

MethodeBeschreibungBeispiel
set()Setzt das Event auf true und weckt alle wartenden Threads aufevent.set()
clear()Setzt das Event auf false zurückevent.clear()
wait(timeout=None)Blockiert, bis das Event true ist oder ein Timeout auftrittevent.wait(2.0)
is_set()Prüft den Event-Statusif event.is_set():

5. Häufige Methoden von Condition

MethodeBeschreibungBeispiel
wait(timeout=None)Gibt die Sperre frei und blockiert, bis benachrichtigt wird oder ein Timeout auftrittcond.wait()
notify(n=1)Weckt höchstensnwartende Threadscond.notify(2)
notify_all()Weckt alle wartenden Threads aufcond.notify_all()

6. Funktionen/Attribute auf Modulebene

Funktion/AttributBeschreibungBeispiel
threading.active_count()Gibt die Anzahl der aktuell aktiven Threads zurückprint(threading.active_count())
threading.current_thread()Gibt das aktuelle Thread-Objekt zurückprint(threading.current_thread().name)
threading.enumerate()Gibt eine Liste aller aktiven Threads zurückfor t in threading.enumerate():
threading.main_thread()Gibt das Haupt-Thread-Objekt zurückif 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

import threading

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

lock = threading.Lock()
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

event = threading.Event()

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

import random
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

  1. 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.

  2. Thread-Sicherheit: Stellen Sie in einer Multithread-Umgebung sicher, dass der Zugriff auf gemeinsam genutzte Ressourcen threadsicher ist, um Datenrennen und Deadlocks zu vermeiden.

  3. 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.


Quiz

Wissensquiz zum Threading-Modul

Frage 1
1. Pythons Global Interpreter Lock (GIL) wirkt sich hauptsächlich auf die Leistung welcher Art von Multithread-Aufgabe aus?
Frage 2
2. Welche Methode sollte nach dem Erstellen eines Thread-Objekts aufgerufen werden, um die Ausführung des Threads zu starten?
Frage 3
3. Was sollte verwendet werden, wenn mehrere Threads dieselbe globale Variable ändern müssen, um Datenbeschädigung zu vermeiden?
Frage 4
4. Wozu dient die Verwendung von thread.join() durch den Hauptthread?
Weitere Erweiterungen