Inter-Thread-Kommunikation in Python meistern: Synchronisation & Datenaustausch
Inter-Thread-Kommunikation bezeichnet den Prozess der Ermöglichung von Kommunikation und Synchronisation zwischen Threads innerhalb eines Python-Multithreading-Programms.
Generell teilen sich Threads in Python denselben Speicherbereich innerhalb eines Prozesses, was ihnen erlaubt, Daten auszutauschen und ihre Aktivitäten über gemeinsame Variablen, Objekte und spezialisierte Synchronisationsmechanismen des threading-Moduls zu koordinieren.
Zur Erleichterung der Inter-Thread-Kommunikation stellt das threading-Modul verschiedene Synchronisationsprimitive bereit: Locks, Events, Conditions und Semaphores. In diesem Tutorial lernen Sie, Event- und Condition-Objekte für die Kommunikation zwischen Threads in einem Multithreading-Programm zu nutzen.
Das Event-Objekt
Ein Event-Objekt verwaltet den Zustand eines internen Flags, sodass Threads warten oder setzen können. Es stellt Methoden bereit, um den Flag-Zustand zu steuern, wodurch Threads ihre Aktivitäten basierend auf geteilten Bedingungen synchronisieren können.
Das Flag ist initial false und wird mit set() auf true, mit clear() auf false gesetzt. wait() blockiert, bis das Flag true ist.
Wichtige Methoden des Event-Objekts
- is_set(): Gibt True zurück, falls das interne Flag true ist.
- set(): Setzt das Flag auf true. Alle wartenden Threads werden geweckt. Threads, die wait() aufrufen, nachdem das Flag true ist, blockieren nicht.
- clear(): Setzt das Flag auf false. Nachfolgende wait()-Aufrufe blockieren, bis set() das Flag wieder auf true setzt.
- wait(timeout=None): Blockiert, bis das Flag true ist. Wenn Flag bei Eintritt true, sofortige Rückkehr. Andernfalls Blockieren bis ein anderer Thread set() aufruft oder optionaler Timeout eintritt. Timeout in Sekunden als Float.
Beispiel: Ampel-Simulation
Der Code simuliert Verkehrsfluss, gesteuert durch Ampelzustand (GRÜN/ROT). Zwei Threads: signal_state() setzt/removed das Event periodisch (Signalwechsel), traffic_flow() wartet auf Event und läuft solange es gesetzt ist.
from threading import Event, Thread import time terminate = False def signal_state(): global terminate while not terminate: time.sleep(0.5) print("Verkehrspolizei gibt GRÜN") event.set() time.sleep(1) print("Verkehrspolizei gibt ROT") event.clear() def traffic_flow(): global terminate num = 0 while num < 10 and not terminate: print("Warte auf GRÜN") event.wait() print("GRÜN ... Verkehr kann fahren") while event.is_set() and not terminate: num += 1 print("Fahrzeug Nr:", num, " überquert Ampel") time.sleep(1) print("ROT ... Verkehr muss warten") event = Event() t1 = Thread(target=signal_state) t2 = Thread(target=traffic_flow) t1.start() t2.start() # Threads nach einiger Zeit beenden time.sleep(5) terminate = True t1.join() t2.join() print("Hauptthread beendet")
Ausgabe
Warte auf GRÜN Verkehrspolizei gibt GRÜN GRÜN ... Verkehr kann fahren Fahrzeug Nr: 1 überquert Ampel Verkehrspolizei gibt ROT ROT ... Verkehr muss warten Warte auf GRÜN Verkehrspolizei gibt GRÜN GRÜN ... Verkehr kann fahren Fahrzeug Nr: 2 überquert Ampel Fahrzeug Nr: 3 überquert Ampel Verkehrspolizei gibt ROT Verkehrspolizei gibt GRÜN Fahrzeug Nr: 4 überquert Ampel Verkehrspolizei gibt ROT ROT ... Verkehr muss warten Verkehrspolizei gibt GRÜN Verkehrspolizei gibt ROT Hauptthread beendet
Das Condition-Objekt
Das Condition-Objekt im threading-Modul bietet einen fortgeschritteneren Synchronisationsmechanismus. Es erlaubt Threads, auf Benachrichtigung eines anderen Threads zu warten, bevor sie fortfahren. Condition-Objekte sind immer mit einem Lock assoziiert und bieten Mechanismen für Signalisierung zwischen Threads.
Syntax
threading.Condition(lock=None)
Wichtige Methoden
- acquire(*args): Erwirbt den zugrundeliegenden Lock.
- release(): Gibt den Lock frei.
- wait(timeout=None): Gibt Lock frei, blockiert bis notify()/notify_all() vom selben Condition in einem anderen Thread, oder Timeout. Danach Lock wieder erwerben und zurückkehren.
- wait_for(predicate, timeout=None): Ruft wait() wiederholt auf, bis Prädikat erfüllt oder Timeout.
- notify(n=1): Weckt maximal n wartende Threads auf.
- notify_all(): Weckt alle wartenden Threads auf. Erfordert erworbenen Lock, sonst RuntimeError.
Beispiel 1: Einfache Benachrichtigung
Thread A wartet auf Benachrichtigung von Thread B. Thread B schläft 2 Sekunden, benachrichtigt dann A.
from threading import Condition, Thread import time c = Condition() def thread_a(): print("Thread A gestartet") with c: print("Thread A wartet auf Erlaubnis...") c.wait() print("Thread A hat Erlaubnis!") print("Thread A beendet") def thread_b(): print("Thread B gestartet") with c: time.sleep(2) print("Benachrichtige Thread A...") c.notify() print("Thread B beendet") Thread(target=thread_a).start() Thread(target=thread_b).start()
Ausgabe:
Thread A gestartet Thread A wartet auf Erlaubnis... Thread B gestartet Benachrichtige Thread A... Thread B beendet Thread A hat Erlaubnis! Thread A beendet
Beispiel 2: Producer-Consumer mit Condition
Thread t2 führt taskA() aus (erzeugt Zufallszahlen, notifiziert), Thread t1 führt taskB() aus (wartet, konsumiert Zahlen).
from threading import Condition, Thread import time import random numbers = [] def taskA(c): for _ in range(5): with c: num = random.randint(1, 10) print("Erzeugte Zufallszahl:", num) numbers.append(num) print("Benachrichtigung gesendet") c.notify() time.sleep(0.3) def taskB(c): for i in range(5): with c: print("Warte auf Update") while not numbers: c.wait() print("Erhaltene Zufallszahl", numbers.pop()) time.sleep(0.3) c = Condition() t1 = Thread(target=taskB, args=(c,)) t2 = Thread(target=taskA, args=(c,)) t1.start() t2.start() t1.join() t2.join() print("Fertig")
Ausgabe:
Warte auf Update Erzeugte Zufallszahl: 2 Benachrichtigung gesendet Erhaltene Zufallszahl 2 Erzeugte Zufallszahl: 5 Benachrichtigung gesendet Warte auf Update Erhaltene Zufallszahl 5 Erzeugte Zufallszahl: 1 Benachrichtigung gesendet Warte auf Update Erhaltene Zufallszahl 1 Erzeugte Zufallszahl: 9 Benachrichtigung gesendet Warte auf Update Erhaltene Zufallszahl 9 Erzeugte Zufallszahl: 2 Benachrichtigung gesendet Warte auf Update Erhaltene Zufallszahl 2 Fertig
Python
- Python-Funktionsargumente meistern: Positional, Keyword & Default-Parameter
- Python-Pakete: Struktur, Importieren und Best Practices
- Python Dictionaries meistern: Syntax, Methoden, Updates, Sortieren & Mergen
- Flask vs. Django: Unterschiede, Features & Wahl des richtigen Frameworks
- Python round()-Funktion erklärt mit praktischen Beispielen
- Python-Typkonvertierung & Casting meistern: Umfassender Leitfaden
- Python Lists meistern: Append, Sort, Length & List Comprehensions (Praxis-Guide)
- Python Queue: FIFO & LIFO verstehen mit praktischen Beispielen
- Python Datei-Handling meistern: Textdateien erstellen, lesen, schreiben und öffnen
- Python-Funktionen meistern: Syntax, Typen & praktische Beispiele