Python - Thread-Pools

Hallo, aufstrebende Python-Programmierer! Heute tauchen wir in die aufregende Welt der Thread-Pools ein. Als euer freundlicher Nachbar und Computerlehrer werde ich euch auf dieser Reise Schritt für Schritt führen. Keine Sorge, wenn ihr neu bei der Programmierung seid; wir beginnen mit den Grundlagen und arbeiten uns nach oben vor. Also, holt euch euer Lieblingsgetränk, macht euch komfortabel und lasst uns unsere Abenteuer beginnen!

Python - Thread Pools

Was sind Thread-Pools?

Bevor wir in den Code springen, lassen uns erst verstehen, was Thread-Pools sind und warum sie wichtig sind. Stellt euch vor, ihr betreibt ein geschäftiges Restaurant. Anstatt für jeden Kunden, der hereinkommt, neue Mitarbeiter zu招聘, habt ihr ein Team von Kellnern, die bereit sind, zu dienen. Dieses Team ist euer "Pool" von Arbeitern. In der Programmierung ist ein Thread-Pool ähnlich - es ist eine Gruppe von wiederverwendbaren Threads, die bereit sind, Arbeit zu leisten, wenn sie benötigt werden.

Thread-Pools helfen uns, mehrere Aufgaben effizient zu verwalten, ohne den Aufwand zu haben, neue Threads für jede Aufgabe zu erstellen. Sie sind besonders nützlich, wenn ihr viele kurzlebige Aufgaben habt, die gleichzeitig ausgeführt werden müssen.

Nun untersuchen wir zwei Hauptwege, Thread-Pools in Python zu implementieren: die Klasse ThreadPool und die Klasse ThreadPoolExecutor.

Verwendung der Python ThreadPool-Klasse

Die Klasse ThreadPool ist Teil des Moduls multiprocessing.pool. Sie ist ein bisschen älter, wird aber immer noch weit verbreitet. Lassen Sie uns sehen, wie wir sie verwenden können:

from multiprocessing.pool import ThreadPool
import time

def worker(num):
print(f"Worker {num} fängt an")
time.sleep(2)  # Simuliere einige Arbeit
print(f"Worker {num} ist fertig")
return num * 2

# Erstelle einen Thread-Pool mit 3 Arbeits-Threads
pool = ThreadPool(3)

# Übergebe 5 Aufgaben an den Pool
results = pool.map(worker, range(5))

# Schließe den Pool und warte auf die Fertigstellung aller Aufgaben
pool.close()
pool.join()

print("Alle Arbeiter sind fertig")
print(f"Ergebnisse: {results}")

Lassen Sie uns das aufbrechen:

  1. Wir importieren ThreadPool und time (für unsere simulierte Arbeit).
  2. Wir definieren eine worker-Funktion, die einige Arbeit simuliert und einen Wert zurückgibt.
  3. Wir erstellen einen ThreadPool mit 3 Arbeits-Threads.
  4. Wir verwenden pool.map(), um 5 Aufgaben an den Pool zu übergeben. Dies verteilt die Aufgaben auf die verfügbaren Threads.
  5. Wir schließen den Pool und warten auf die Fertigstellung aller Aufgaben.
  6. Schließlich drucken wir die Ergebnisse aus.

Wenn ihr das ausführt, werdet ihr sehen, dass obwohl wir 5 Aufgaben haben, sie von 3 Arbeits-Threads ausgeführt werden, was zeigt, wie der Thread-Pool die Workload verwaltet.

Verwendung der Python ThreadPoolExecutor-Klasse

Nun schauen wir uns die modernere Klasse ThreadPoolExecutor aus dem Modul concurrent.futures an. Diese Klasse bietet eine höhere Schnittstelle für die asynchrone Ausführung von Callables.

from concurrent.futures import ThreadPoolExecutor
import time

def worker(num):
print(f"Worker {num} fängt an")
time.sleep(2)  # Simuliere einige Arbeit
print(f"Worker {num} ist fertig")
return num * 2

# Erstelle einen ThreadPoolExecutor mit 3 Arbeits-Threads
with ThreadPoolExecutor(max_workers=3) as executor:
# Übergebe 5 Aufgaben an den Executor
futures = [executor.submit(worker, i) for i in range(5)]

# Warte auf die Fertigstellung aller Aufgaben und erhalte Ergebnisse
results = [future.result() for future in futures]

print("Alle Arbeiter sind fertig")
print(f"Ergebnisse: {results}")

Lassen Sie uns dieses Beispiel aufbrechen:

  1. Wir importieren ThreadPoolExecutor anstelle von ThreadPool.
  2. Wir verwenden eine with-Anweisung, um den Executor zu erstellen und zu verwalten. Dies stellt sicher, dass nach dem Abschluss eine ordnungsgemäße Bereinigung erfolgt.
  3. Wir verwenden executor.submit(), um einzelne Aufgaben an den Pool zu übergeben.
  4. Wir erstellen eine Liste von Future-Objekten, die die endgültigen Ergebnisse unserer Aufgaben darstellen.
  5. Wir verwenden future.result(), um auf die Ergebnisse jeder Aufgabe zu warten und abzurufen.

Der ThreadPoolExecutor bietet mehr Flexibilität und ist im Allgemeinen einfacher zu verwenden, insbesondere für komplexere Szenarien.

Vergleich von ThreadPool und ThreadPoolExecutor

Lassen Sie uns diese beiden Ansätze vergleichen:

Eigenschaft ThreadPool ThreadPoolExecutor
Modul multiprocessing.pool concurrent.futures
Python-Version Alle Versionen 3.2 und später
Kontext-Manager Nein Ja
Flexibilität Weniger Mehr
Fehlerbehandlung Basic Fortgeschritten
Stornierung Eingeschränkt Unterstützt
Future-Objekte Nein Ja

Wie ihr seht, bietet der ThreadPoolExecutor mehr Funktionen und ist im Allgemeinen flexibler. Allerdings ist der ThreadPool immer noch nützlich, insbesondere wenn ihr mit älteren Python-Versionen arbeitet oder Kompatibilität mit bestehendem Code aufrechterhalten müsst.

Best Practices und Tipps

  1. Wähle die richtige Anzahl von Threads: Zu wenige Threads nutzen vielleicht nicht vollständig eure CPU, während zu viele zu Overhead führen können. Ein guter Ausgangspunkt ist die Anzahl der CPU-Kerne auf eurer Maschine.

  2. Verwende Kontext-Manager: Verwende bei ThreadPoolExecutor immer die with-Anweisung, um sicherzustellen, dass eine ordnungsgemäße Bereinigung erfolgt.

  3. Behandle Ausnahmen: Stelle sicher, dass ihr Ausnahmen in euren Arbeiter-Funktionen behandelt, um stillschweigende Fehler zu vermeiden.

  4. Sei vorsichtig mit gemeinsamen Ressourcen: Wenn du Thread-Pools verwendest, sei vorsichtig mit gemeinsamen Ressourcen, um Renndurchläufe zu vermeiden.

  5. Berücksichtige die Granularität der Aufgaben: Thread-Pools funktionieren am besten mit vielen kleinen Aufgaben plutôt als mit wenigen großen.

Fazit

Herzlichen Glückwunsch! Ihr habt gerade eure ersten Schritte in die Welt der Thread-Pools in Python gemacht. Wir haben die Grundlagen von ThreadPool und ThreadPoolExecutor behandelt, und ihr solltet nun eine gute Grundlage haben, um diese leistungsfähigen Tools in euren eigenen Projekten zu verwenden.

Denkt daran, wie man in einer geschäftigen Restaurantküche lernt zu kochen, das Meistern der Thread-Pools erfordert Übung. Habt nicht Angst, zu experimentieren und Fehler zu machen - so lernen wir! Fortsetzt mit dem Coden, fortsetzt mit dem Lernen, und bevor ihr es wischt, werdet ihr mit Threads um sich werfen wie ein Profi-Koch mit Tellern in einer geschäftigen Küche.

Happy coding, und möge eure Threads immer in Harmonie sein!

Credits: Image by storyset