Python Multithreading: Effiziente Nebenläufigkeitstechniken
Vorteile von Threads
Das Ausführen mehrerer Threads ähnelt dem gleichzeitigen Ausführen mehrerer Programme, bietet aber Vorteile:
- Threads innerhalb eines Prozesses teilen denselben Datenraum mit dem Hauptthread und können daher leichter Informationen austauschen oder kommunizieren als separate Prozesse.
- Threads (Leichtgewichtsprozesse) benötigen wenig Speicheroverhead; sie sind „billiger“ als Prozesse.
Ein Thread hat einen Anfang, eine Ausführungssequenz und ein Ende. Er besitzt einen Instruktionszeiger, der verfolgt, wo er sich in seinem Kontext befindet.
- Er kann präemptiert (unterbrochen) werden.
- Er kann temporär pausiert werden (Sleeping), während andere Threads laufen – dies nennt man Yielding.
Einen neuen Thread starten
Um einen weiteren Thread zu erzeugen, rufen Sie folgende Methode aus dem thread-Modul auf (Python 2; in Python 3 heißt es _thread):
thread.start_new_thread(function, args[, kwargs])
Dies ermöglicht eine schnelle und effiziente Thread-Erstellung unter Linux und Windows. Der Aufruf kehrt sofort zurück; der Kindthread startet und ruft function mit der übergebenen args-Liste auf. Wenn die Funktion zurückkehrt, endet der Thread.
args ist ein Tupel von Argumenten; leeres Tupel für Aufruf ohne Argumente. kwargs ist ein optionales Dictionary für Keyword-Argumente.
Beispiel
#!/usr/bin/python
import thread
import time
def print_time(threadName, delay):
count = 0
while count < 5:
time.sleep(delay)
count += 1
print("%s: %s" % (threadName, time.ctime(time.time())))
try:
thread.start_new_thread(print_time, ("Thread-1", 2))
thread.start_new_thread(print_time, ("Thread-2", 4))
except:
print("Error: unable to start thread")
while 1:
pass
Ausgabe (Beispiel):
Thread-1: Thu Jan 22 15:42:17 2009 Thread-1: Thu Jan 22 15:42:19 2009 Thread-2: Thu Jan 22 15:42:19 2009 Thread-1: Thu Jan 22 15:42:21 2009 Thread-2: Thu Jan 22 15:42:23 2009 Thread-1: Thu Jan 22 15:42:23 2009 Thread-1: Thu Jan 22 15:42:25 2009 Thread-2: Thu Jan 22 15:42:27 2009 Thread-2: Thu Jan 22 15:42:31 2009 Thread-2: Thu Jan 22 15:42:35 2009
Obwohl effektiv für Low-Level-Threading, ist das thread-Modul im Vergleich zum neueren threading-Modul sehr begrenzt.
Das threading-Modul
Das neuere threading-Modul (ab Python 2.4) bietet leistungsfähigere, höherwertige Thread-Unterstützung.
Es stellt alle Methoden des thread-Moduls bereit plus zusätzliche:
threading.activeCount()– Anzahl aktiver Thread-Objekte.threading.currentThread()– Thread-Objekt des Aufrufers.threading.enumerate()– Liste aller aktuell aktiven Thread-Objekte.
Zusätzlich definiert es die Thread-Klasse mit folgenden Methoden:
run()– Einstiegspunkt für einen Thread.start()– Startet Thread durch Aufruf vonrun().join([time])– Wartet auf Thread-Beendigung.isAlive()– Prüft, ob Thread noch läuft.getName()– Gibt Threadnamen zurück.setName()– Setzt Threadnamen.
Thread mit threading erstellen
Schritte:
- Neue Unterklasse von
Threaddefinieren. __init__(self[, args])überschreiben für zusätzliche Argumente.run(self[, args])überschreiben für Thread-Logik.
Instanz erstellen und start() aufrufen (ruft intern run() auf).
Beispiel
#!/usr/bin/python
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
print_time(self.name, 5, self.counter)
print("Exiting " + self.name)
def print_time(threadName, counter, delay):
while counter:
if exitFlag:
threadName.exit()
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
print("Exiting Main Thread")
Ausgabe (Beispiel):
Starting Thread-1 Starting Thread-2 Exiting Main Thread Thread-1: Thu Mar 21 09:10:03 2013 Thread-1: Thu Mar 21 09:10:04 2013 Thread-2: Thu Mar 21 09:10:04 2013 Thread-1: Thu Mar 21 09:10:05 2013 Thread-1: Thu Mar 21 09:10:06 2013 Thread-2: Thu Mar 21 09:10:06 2013 Thread-1: Thu Mar 21 09:10:07 2013 Exiting Thread-1 Thread-2: Thu Mar 21 09:10:08 2013 Thread-2: Thu Mar 21 09:10:10 2013 Thread-2: Thu Mar 21 09:10:12 2013 Exiting Thread-2
Threads synchronisieren
Das threading-Modul bietet einen einfachen Locking-Mechanismus zur Synchronisation.
- Neuer Lock:
Lock(). acquire(blocking)– Erzwingt synchrones Ausführen.blocking=0: sofortige Rückgabe (0 = nicht erworben, 1 = erworben).blocking=1: Blockiert bis Lock freigegeben.release()– Gibt Lock frei.
Beispiel
#!/usr/bin/python
import threading
import time
class myThread(threading.Thread):
def __init__(self, threadID, name, counter):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.counter = counter
def run(self):
print("Starting " + self.name)
threadLock.acquire()
print_time(self.name, self.counter, 3)
threadLock.release()
def print_time(threadName, delay, counter):
while counter:
time.sleep(delay)
print("%s: %s" % (threadName, time.ctime(time.time())))
counter -= 1
threadLock = threading.Lock()
threads = []
thread1 = myThread(1, "Thread-1", 1)
thread2 = myThread(2, "Thread-2", 2)
thread1.start()
thread2.start()
threads.append(thread1)
threads.append(thread2)
for t in threads:
t.join()
print("Exiting Main Thread")
Ausgabe (Beispiel):
Starting Thread-1 Starting Thread-2 Thread-1: Thu Mar 21 09:11:28 2013 Thread-1: Thu Mar 21 09:11:29 2013 Thread-1: Thu Mar 21 09:11:30 2013 Thread-2: Thu Mar 21 09:11:32 2013 Thread-2: Thu Mar 21 09:11:34 2013 Thread-2: Thu Mar 21 09:11:36 2013 Exiting Main Thread
Multithreaded Priority Queue
Das Queue-Modul (Python 3: queue) erstellt Queue-Objekte mit fester Kapazität.
get()– Entfernt und gibt Item zurück.put()– Fügt Item hinzu.qsize()– Aktuelle Item-Anzahl.empty()– True, wenn leer.full()– True, wenn voll.
Beispiel
#!/usr/bin/python
import Queue
import threading
import time
exitFlag = 0
class myThread(threading.Thread):
def __init__(self, threadID, name, q):
threading.Thread.__init__(self)
self.threadID = threadID
self.name = name
self.q = q
def run(self):
print("Starting " + self.name)
process_data(self.name, self.q)
print("Exiting " + self.name)
def process_data(threadName, q):
while not exitFlag:
queueLock.acquire()
if not workQueue.empty():
data = q.get()
queueLock.release()
print("%s processing %s" % (threadName, data))
else:
queueLock.release()
time.sleep(1)
threadList = ["Thread-1", "Thread-2", "Thread-3"]
nameList = ["One", "Two", "Three", "Four", "Five"]
queueLock = threading.Lock()
workQueue = Queue.Queue(10)
threads = []
threadID = 1
for tName in threadList:
thread = myThread(threadID, tName, workQueue)
thread.start()
threads.append(thread)
threadID += 1
queueLock.acquire()
for word in nameList:
workQueue.put(word)
queueLock.release()
while not workQueue.empty():
pass
exitFlag = 1
for t in threads:
t.join()
print("Exiting Main Thread")
Ausgabe (Beispiel):
Starting Thread-1 Starting Thread-2 Starting Thread-3 Thread-1 processing One Thread-2 processing Two Thread-3 processing Three Thread-1 processing Four Thread-2 processing Five Exiting Thread-3 Exiting Thread-1 Exiting Thread-2 Exiting Main Thread
Python
- Gewindeformen vs. Gewindewalzen: Die wesentlichen Unterschiede verstehen
- Python-Datentypen meistern: Praxisleitfaden
- Python-Operatoren meistern: Umfassender Leitfaden
- Python pass-Statement meistern: Praxisleitfaden
- Python-Funktionsargumente meistern: Positional, Keyword & Default-Parameter
- Python-Dictionaries meistern: Erstellung, Manipulation und fortgeschrittene Techniken
- Python Objektorientierte Programmierung meistern: Klassen, Vererbung, Kapselung & Polymorphie
- Python Netzwerkprogrammierung: Sockets & erweiterte Protokolle meistern
- Python Extension Programming mit C: Schnelle, native Module erstellen
- Fanuc G76 Gewindezyklus meistern: Kompletter Leitfaden für CNC-Zerspaner