Python-Threading: Eine Einführung – wdzwdz

In diesem Lernprogramm erfahren Sie, wie Sie das integrierte Threading-Modul von Python verwenden, um die Multithreading-Funktionen in Python zu erkunden.

Beginnend mit den Grundlagen von Prozessen und Threads lernen Sie, wie Multithreading in Python funktioniert, und verstehen gleichzeitig die Konzepte von Nebenläufigkeit und Parallelität. Anschließend erfahren Sie, wie Sie mithilfe des integrierten Threading-Moduls einen oder mehrere Threads in Python starten und ausführen.

Lass uns anfangen.

Prozesse vs. Threads: Was sind die Unterschiede?

Was ist ein Prozess?

Ein Prozess ist jede Instanz eines Programms, das ausgeführt werden muss.

Es kann alles sein – ein Python-Skript oder ein Webbrowser wie Chrome bis hin zu einer Videokonferenzanwendung. Wenn Sie den Task-Manager auf Ihrem Computer starten und zu Leistung -> CPU navigieren, können Sie die Prozesse und Threads sehen, die derzeit auf Ihren CPU-Kernen ausgeführt werden.

Prozesse und Threads verstehen

Intern hat ein Prozess einen dedizierten Speicher, der den Code und die Daten speichert, die dem Prozess entsprechen.

Ein Prozess besteht aus einem oder mehreren Threads. Ein Thread ist die kleinste Folge von Anweisungen, die das Betriebssystem ausführen kann, und stellt den Ausführungsfluss dar.

Jeder Thread hat seinen eigenen Stack und Register, aber keinen dedizierten Speicher. Alle einem Prozess zugeordneten Threads können auf die Daten zugreifen. Daher werden Daten und Speicher von allen Threads eines Prozesses gemeinsam genutzt.

In einer CPU mit N Kernen können N Prozesse zur selben Zeit parallel ausgeführt werden. Zwei Threads desselben Prozesses können jedoch niemals parallel ausgeführt werden, sondern können gleichzeitig ausgeführt werden. Wir werden uns im nächsten Abschnitt mit dem Konzept der Nebenläufigkeit vs. Parallelität befassen.

Lassen Sie uns basierend auf dem, was wir bisher gelernt haben, die Unterschiede zwischen einem Prozess und einem Thread zusammenfassen.

FeatureProcessThreadMemoryDedicated MemoryShared MemoryAusführungsmodusParallel, concurrentConcurrent; aber nicht parallelExecution behandelt von Operating SystemCPython Interpreter

Multithreading in Python

In Python stellt die Global Interpreter Lock (GIL) sicher, dass zu jedem Zeitpunkt nur ein Thread die Sperre erwerben und ausführen kann. Alle Threads sollten diese Sperre erwerben, um ausgeführt zu werden. Dadurch wird sichergestellt, dass zu einem bestimmten Zeitpunkt nur ein einziger Thread ausgeführt werden kann, und gleichzeitiges Multithreading wird vermieden.

Stellen Sie sich beispielsweise zwei Threads, t1 und t2, desselben Prozesses vor. Da Threads dieselben Daten gemeinsam nutzen, wenn t1 einen bestimmten Wert k liest, kann t2 denselben Wert k ändern. Dies kann zu Deadlocks und unerwünschten Ergebnissen führen. Aber nur einer der Threads kann die Sperre erwerben und in jeder Instanz ausgeführt werden. Daher sorgt GIL auch für Fadensicherheit.

  So bearbeiten Sie Nachrichten auf iPhone, iPad und Mac

Wie erreichen wir also Multithreading-Fähigkeiten in Python? Um dies zu verstehen, wollen wir die Konzepte Parallelität und Parallelität diskutieren.

Nebenläufigkeit vs. Parallelität: Ein Überblick

Stellen Sie sich eine CPU mit mehr als einem Kern vor. In der Abbildung unten hat die CPU vier Kerne. Das bedeutet, dass wir zu jedem Zeitpunkt vier verschiedene Operationen parallel ausführen können.

Wenn es vier Prozesse gibt, kann jeder der Prozesse unabhängig und gleichzeitig auf jedem der vier Kerne laufen. Nehmen wir an, dass jeder Prozess zwei Threads hat.

Um zu verstehen, wie Threading funktioniert, wechseln wir von der Multicore- zur Singlecore-Prozessorarchitektur. Wie erwähnt, kann bei einer bestimmten Ausführungsinstanz nur ein einziger Thread aktiv sein; aber der Prozessorkern kann zwischen den Threads umschalten.

E/A-gebundene Threads warten beispielsweise häufig auf E/A-Operationen: Einlesen von Benutzereingaben, Datenbanklesevorgänge und Dateioperationen. Während dieser Wartezeit kann er die Sperre freigeben, damit der andere Thread ausgeführt werden kann. Die Wartezeit kann auch eine einfache Operation sein, wie z. B. Schlafen für n Sekunden.

Zusammengefasst: Während Warteoperationen gibt der Thread die Sperre frei, wodurch der Prozessorkern auf einen anderen Thread umschalten kann. Der frühere Thread nimmt die Ausführung nach Ablauf der Wartezeit wieder auf. Dieser Prozess, bei dem der Prozessorkern gleichzeitig zwischen den Threads umschaltet, erleichtert Multithreading. ✅

Wenn Sie Parallelität auf Prozessebene in Ihrer Anwendung implementieren möchten, sollten Sie stattdessen die Verwendung von Multiprocessing in Betracht ziehen.

Python-Threading-Modul: Erste Schritte

Python wird mit einem Threading-Modul geliefert, das Sie in das Python-Skript importieren können.

import threading

Um ein Thread-Objekt in Python zu erstellen, können Sie den Thread-Konstruktor verwenden: threading.Thread(…). Dies ist die generische Syntax, die für die meisten Threading-Implementierungen ausreicht:

threading.Thread(target=...,args=...)

Hier,

  • target ist das Schlüsselwortargument, das eine aufrufbare Python-Datei bezeichnet
  • args ist das Tupel von Argumenten, die das Ziel aufnimmt.

Sie benötigen Python 3.x, um die Codebeispiele in diesem Tutorial auszuführen. Laden Sie den Code herunter und folgen Sie ihm.

So definieren und führen Sie Threads in Python aus

Lassen Sie uns einen Thread definieren, der eine Zielfunktion ausführt.

Die Zielfunktion ist some_func.

import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
print(threading.active_count())

Lassen Sie uns analysieren, was das obige Code-Snippet tut:

  • Es importiert das Threading und die Zeitmodule.
  • Die Funktion some_func hat beschreibende print()-Anweisungen und beinhaltet eine Schlafoperation für zwei Sekunden: time.sleep(n) bewirkt, dass die Funktion n Sekunden lang schläft.
  • Als nächstes definieren wir einen Thread thread_1 mit dem Ziel some_func. threading.Thread(target=…) erstellt ein Thread-Objekt.
  • Hinweis: Geben Sie den Namen der Funktion und keinen Funktionsaufruf an; Verwenden Sie some_func und nicht some_func().
  • Das Erstellen eines Thread-Objekts startet keinen Thread; Der Aufruf der start()-Methode für das Thread-Objekt funktioniert.
  • Um die Anzahl der aktiven Threads zu erhalten, verwenden wir die Funktion active_count().
  9 Website-Wertrechner, um den wahren Wert Ihrer Domain zu ermitteln

Das Python-Skript wird im Hauptthread ausgeführt, und wir erstellen einen weiteren Thread (thread1), um die Funktion some_func auszuführen, sodass die Anzahl der aktiven Threads zwei beträgt, wie in der Ausgabe zu sehen ist:

# Output
Running some_func...
2
Finished running some_func.

Wenn wir uns die Ausgabe genauer ansehen, sehen wir, dass beim Start von Thread1 die erste print-Anweisung ausgeführt wird. Aber während des Sleep-Vorgangs wechselt der Prozessor zum Haupt-Thread und gibt die Anzahl der aktiven Threads aus – ohne darauf zu warten, dass Thread1 die Ausführung beendet.

Warten, bis Threads die Ausführung beenden

Wenn Sie möchten, dass Thread1 die Ausführung beendet, können Sie nach dem Start des Threads die Methode join() dafür aufrufen. Dadurch wird darauf gewartet, dass Thread1 die Ausführung beendet, ohne zum Hauptthread zu wechseln.

import threading
import time

def some_func():
    print("Running some_func...")
    time.sleep(2)
    print("Finished running some_func.")

thread1 = threading.Thread(target=some_func)
thread1.start()
thread1.join()
print(threading.active_count())

Jetzt hat Thread1 die Ausführung beendet, bevor wir die Anzahl der aktiven Threads ausgeben. Es läuft also nur der Haupt-Thread, was bedeutet, dass die Anzahl der aktiven Threads eins ist. ✅

# Output
Running some_func...
Finished running some_func.
1

So führen Sie mehrere Threads in Python aus

Als Nächstes erstellen wir zwei Threads, um zwei verschiedene Funktionen auszuführen.

Hier ist count_down eine Funktion, die eine Zahl als Argument akzeptiert und von dieser Zahl bis Null herunterzählt.

def count_down(n):
    for i in range(n,-1,-1):
        print(i)

Wir definieren count_up, eine weitere Python-Funktion, die von Null bis zu einer bestimmten Zahl zählt.

def count_up(n):
    for i in range(n+1):
        print(i)

📑 Bei Verwendung der Funktion range() mit der Syntax range(start, stop, step) wird der Endpunkt stop standardmäßig ausgeschlossen.

– Um von einer bestimmten Zahl bis Null herunterzuzählen, können Sie einen negativen Schrittwert von -1 verwenden und den Stoppwert auf -1 setzen, sodass Null enthalten ist.

  Wie (und warum) startet man Microsoft Word über die Eingabeaufforderung?

– Ebenso müssen Sie, um bis n hochzuzählen, den Stoppwert auf n + 1 setzen. Da die Standardwerte von start und step 0 bzw. 1 sind, können Sie range(n + 1) verwenden, um die Sequenz 0 zu erhalten durch n.

Als Nächstes definieren wir zwei Threads, Thread1 und Thread2, um die Funktionen count_down bzw. count_up auszuführen. Wir fügen print-Anweisungen und Sleep-Operationen für beide Funktionen hinzu.

Beachten Sie beim Erstellen der Thread-Objekte, dass die Argumente für die Zielfunktion als Tupel angegeben werden sollten – für den args-Parameter. Da beide Funktionen (count_down und count_up) ein Argument aufnehmen. Sie müssen explizit ein Komma nach dem Wert einfügen. Dadurch wird sichergestellt, dass das Argument weiterhin als Tupel übergeben wird, da die nachfolgenden Elemente als None abgeleitet werden.

import threading
import time

def count_down(n):
    for i in range(n,-1,-1):
        print("Running thread1....")
        print(i)
        time.sleep(1)


def count_up(n):
    for i in range(n+1):
        print("Running thread2...")
        print(i)
        time.sleep(1)

thread1 = threading.Thread(target=count_down,args=(10,))
thread2 = threading.Thread(target=count_up,args=(5,))
thread1.start()
thread2.start()

In der Ausgabe:

  • Die Funktion count_up läuft auf thread2 und zählt von 0 beginnend bis 5 hoch.
  • Die count_down-Funktion läuft auf Thread1 und zählt von 10 auf 0 herunter.
# Output
Running thread1....
10
Running thread2...
0
Running thread1....
9
Running thread2...
1
Running thread1....
8
Running thread2...
2
Running thread1....
7
Running thread2...
3
Running thread1....
6
Running thread2...
4
Running thread1....
5
Running thread2...
5
Running thread1....
4
Running thread1....
3
Running thread1....
2
Running thread1....
1
Running thread1....
0

Sie können sehen, dass Thread1 und Thread2 alternativ ausgeführt werden, da beide eine Warteoperation (sleep) beinhalten. Sobald die count_up-Funktion das Zählen bis 5 beendet hat, ist Thread2 nicht mehr aktiv. Wir erhalten also die Ausgabe, die nur Thread1 entspricht.

Zusammenfassen

In diesem Lernprogramm haben Sie gelernt, wie Sie das integrierte Threading-Modul von Python verwenden, um Multithreading zu implementieren. Hier ist eine Zusammenfassung der wichtigsten Takeaways:

  • Der Thread-Konstruktor kann verwendet werden, um ein Thread-Objekt zu erstellen. Durch die Verwendung von threading.Thread(target=,args=()) wird ein Thread erstellt, der das aufrufbare Ziel mit in args angegebenen Argumenten ausführt.
  • Das Python-Programm wird auf einem Haupt-Thread ausgeführt, sodass die von Ihnen erstellten Thread-Objekte zusätzliche Threads sind. Sie können die Funktion active_count() aufrufen, die die Anzahl der aktiven Threads in jeder Instanz zurückgibt.
  • Sie können einen Thread mit der start()-Methode für das Thread-Objekt starten und warten, bis die Ausführung mit der join()-Methode abgeschlossen ist.

Sie können zusätzliche Beispiele codieren, indem Sie die Wartezeiten anpassen, eine andere E/A-Operation ausprobieren und vieles mehr. Achten Sie darauf, Multithreading in Ihren kommenden Python-Projekten zu implementieren. Viel Spaß beim Programmieren!🎉

x