Multithreading in Python [mit Codierungsbeispielen]
Veröffentlicht: 2020-11-30Das Verbessern und Beschleunigen von Code ist der nächste Schritt, nachdem die Grundkenntnisse von Python erworben wurden. Multithreading ist eine Möglichkeit, diese Optimierung mithilfe von „Threads“ zu erreichen. Was sind das für Fäden? Und wie unterscheiden sich diese von Prozessen? Lass es uns herausfinden.

Am Ende dieses Tutorials verfügen Sie über folgende Kenntnisse:
- Was sind Threads und Prozesse?
- Wie wird Multithreading erreicht?
- Was sind seine Einschränkungen?
- Wann sollte Multithreading verwendet werden?
Lernen Sie Online-Data-Science-Kurse von den besten Universitäten der Welt. Verdienen Sie Executive PG-Programme, Advanced Certificate-Programme oder Master-Programme, um Ihre Karriere zu beschleunigen.
Inhaltsverzeichnis
Threads in Python
Wenn wir an Multitasking denken, denken wir an parallele Ausführung. Multithreading nicht streng parallele Ausführung. Threads können als separate Entitäten des Ausführungsflusses verschiedener Teile Ihres Programms betrachtet werden, die unabhängig voneinander ausgeführt werden. Threads werden also im Wesentlichen nicht parallel ausgeführt, aber Python wechselt so schnell von einem Thread zum anderen, dass es scheint, als wären sie parallel.
Prozesse hingegen sind streng parallel und laufen auf verschiedenen Kernen, um eine schnellere Ausführung zu erreichen. Threads können auch auf verschiedenen Prozessoren ausgeführt werden, aber sie werden technisch immer noch nicht parallel ausgeführt.
Und denken Sie, wenn Threads nicht parallel laufen, wie machen sie die Dinge dann schneller? Die Antwort ist, dass sie die Verarbeitung nicht immer beschleunigen. Multithreading wird speziell bei Aufgaben verwendet, bei denen Threads die Verarbeitung beschleunigen.
Alle Informationen eines Threads sind im Thread Control Block (TCB) enthalten . TCB besteht aus den folgenden Hauptteilen:
- Ein eindeutiger TID – Thread Identifier
- Stack Pointer, der im Prozess auf den Stack des Threads zeigt
- Ein Programmzähler, der die Adresse der aktuell vom Thread ausgeführten Anweisung speichert
- Zustand des Threads (läuft, bereit, wartet, startet oder erledigt)
Allerdings können Prozesse mehrere Threads enthalten, die den Code, die Daten und alle Dateien gemeinsam nutzen. Und alle Threads haben ihre eigenen separaten Register und Stacks, auf die sie Zugriff haben.
Jetzt fragen Sie sich vielleicht, wenn die Threads die gemeinsamen Daten und den gemeinsamen Code verwenden, wie können sie ihn alle verwenden, ohne andere Threads zu behindern. Dies ist die größte Einschränkung von Multithreading, über die wir später in diesem Tutorial sprechen werden.
Kontextwechsel
Nun, wie oben beschrieben, laufen Threads nicht parallel, sondern konsequent. Wenn also ein Thread T1 mit der Ausführung beginnt, bleiben alle anderen Threads im Wartemodus. Erst nachdem T1 mit seiner Ausführung fertig ist, kann jeder andere Thread in der Warteschlange mit der Ausführung beginnen. Python wechselt so schnell von einem Thread zum anderen, dass es wie eine parallele Ausführung erscheint. Dieses Umschalten nennen wir „Context Switching“.
Multithreaded-Programmierung
Betrachten Sie den folgenden Code, der Threads verwendet, um eine Würfel- und eine Quadratoperation auszuführen.
| Gewinde importieren def Würfel (n) : print( "Würfel: {}" .format(n * n * n)) def Quadratur (n) : print( "Quadrat: {}" .format(n * n)) wenn __name__ == „__main__“ : # Erstellen Sie den Thread t1 = threading.Thread(target=squarer, args=( 5 ,)) t2 = threading.Thread(target=cuber, args=( 5 ,)) # Starte den Thread t1 t1.start() # Starte den Thread t2 t2.start() # warten bis t1 abgeschlossen ist t1.join() # warten bis t2 abgeschlossen ist t2.join() # beide Threads abgeschlossen print( "Fertig!" ) |
| #Ausgabe: Quadrat: 25 Würfel: 125 Getan! |
Versuchen wir nun, den Code zu verstehen.
Zuerst importieren wir das Threading-Modul, das für alle Aufgaben zuständig ist. Innerhalb der Hauptklasse erstellen wir zwei Threads, indem wir Unterklassen der Thread-Klasse erstellen. Wir müssen das Ziel übergeben, das die Funktion ist, die in diesem Thread ausgeführt werden muss, und die Argumente, die an diese Funktionen übergeben werden müssen.
Sobald die Threads deklariert sind, müssen wir sie starten. Dies geschieht durch Aufrufen der start -Methode für Threads. Nach dem Start muss das Hauptprogramm warten, bis die Threads ihre Verarbeitung abgeschlossen haben. Wir verwenden die Wait -Methode, um das Hauptprogramm anzuhalten und darauf zu warten, dass die Threads T1 und T2 ihre Ausführung beenden.
Muss gelesen werden: Python-Herausforderungen für Anfänger
Thread-Synchronisierung
Wie wir oben besprochen haben, werden Threads nicht parallel ausgeführt, stattdessen wechselt Python von einem zum anderen. Daher ist eine korrekte Synchronisierung zwischen den Threads sehr wichtig, um seltsames Verhalten zu vermeiden.
Rennbedingung
Threads, die demselben Prozess unterliegen, verwenden gemeinsame Daten und Dateien, was zu einem „Wettlauf“ um die Daten zwischen mehreren Threads führen kann. Wenn also von mehreren Threads auf ein Datenelement zugegriffen wird, wird es von beiden Threads geändert, und die Ergebnisse, die wir erhalten, sind nicht wie erwartet. Dies wird als Race Condition bezeichnet.
Wenn Sie also zwei Threads haben, die Zugriff auf dieselben Daten haben, können beide darauf zugreifen und sie ändern, wenn dieser bestimmte Thread ausgeführt wird. Wenn also T1 mit der Ausführung beginnt und einige Daten ändert, befindet sich T2 im Schlaf-/Wartemodus. Dann stoppt T1 die Ausführung und geht in den Schlafmodus, wobei die Steuerung an T2 übergeben wird, das auch Zugriff auf dieselben Daten hat. Daher wird T2 nun dieselben Daten modifizieren und überschreiben, was zu Problemen führen wird, wenn T1 erneut beginnt.

Das Ziel der Thread-Synchronisierung besteht darin, sicherzustellen, dass diese Race-Condition niemals auftritt und auf den kritischen Codeabschnitt von Threads nacheinander synchronisiert zugegriffen wird.
Schlösser
Um die Race Condition und ihre Folgen zu lösen und zu verhindern, bietet das Thread-Modul eine Lock -Klasse, die Semaphoren verwendet, um Threads bei der Synchronisierung zu unterstützen. Semaphoren sind nichts anderes als binäre Flags. Betrachten Sie sie als „Besetzt“-Schild an Telefonzellen, die den Wert „Besetzt“ (entspricht 1) oder „Nicht besetzt“ (entspricht 0) haben. Jedes Mal, wenn ein Thread auf ein Codesegment mit Sperre stößt, muss er prüfen, ob sich die Sperre bereits im 1-Zustand befindet. Wenn dies der Fall ist, muss es warten, bis es 0 wird, damit es es verwenden kann.
Die Lock -Klasse hat zwei primäre Methoden:
- erwerben([blockieren]) : Die Methode " erwerben " übernimmt den Parameter " blocking " entweder als " True " oder " False " . Wenn eine Sperre für einen Thread T1 mit Blockierung als True initiiert wurde, wartet sie oder bleibt blockiert, bis der kritische Codeabschnitt von einem anderen Thread T2 gesperrt wird. Sobald der andere Thread T2 die Sperre freigibt, erwirbt Thread T1 die Sperre und gibt True zurück .
Wenn andererseits die Sperre für Thread T1 mit Parameterblockierung als False initiiert wurde , wartet der Thread T1 nicht oder bleibt blockiert, wenn der kritische Abschnitt bereits von Thread T2 gesperrt ist. Wenn es es als gesperrt ansieht, gibt es sofort False zurück und beendet sich. Wenn der Code jedoch nicht von einem anderen Thread gesperrt wurde, erwirbt er die Sperre und gibt True zurück .
release() : Wenn die release-Methode für die Sperre aufgerufen wird, entsperrt sie die Sperre und gibt True zurück. Außerdem wird überprüft, ob irgendwelche Threads darauf warten, dass die Sperre freigegeben wird. Wenn dies der Fall ist, wird genau einem von ihnen der Zugriff auf das Schloss ermöglicht.
Wenn die Sperre jedoch bereits entsperrt ist, wird ein ThreadError ausgelöst.
Blockaden
Ein weiteres Problem, das beim Umgang mit mehreren Sperren auftritt, ist – Deadlocks. Deadlocks treten auf, wenn Sperren aus verschiedenen Gründen nicht von Threads freigegeben werden. Betrachten wir ein einfaches Beispiel, in dem wir Folgendes tun:
| Gewinde importieren l = threading.Lock() # Vor dem 1. Erwerb l.erwerben() # Vor dem 2. Erwerb l.erwerben() # Erhielt die Sperre jetzt zweimal |
Im obigen Code rufen wir die Methode „acquire“ zweimal auf, geben sie aber nicht frei, nachdem sie zum ersten Mal erfasst wurde. Wenn Python also die zweite Erwerbsanweisung sieht, geht es auf unbestimmte Zeit in den Wartemodus, da wir die vorherige Sperre nie aufgehoben haben.
Diese Deadlock-Bedingungen können sich in Ihren Code einschleichen, ohne dass Sie es merken. Selbst wenn Sie einen Freigabeaufruf einfügen, kann Ihr Code auf halbem Weg fehlschlagen und die Freigabe wird nie aufgerufen und die Sperre bleibt gesperrt. Eine Möglichkeit, dies zu umgehen, ist die Verwendung der with – as -Anweisung, die auch als Kontextmanager bezeichnet wird. Mit der with – as -Anweisung wird die Sperre automatisch aufgehoben, sobald die Verarbeitung beendet ist oder aus irgendeinem Grund fehlgeschlagen ist.
Lesen Sie: Ideen und Themen für Python-Projekte
Bevor du gehst
Wie wir bereits besprochen haben, ist Multithreading nicht in allen Anwendungen nützlich, da es die Dinge nicht wirklich parallel laufen lässt. Aber die Hauptanwendung von Multithreading sind E/A-Aufgaben, bei denen die CPU untätig bleibt, während sie darauf wartet, dass Daten geladen werden. Multithreading spielt hier eine entscheidende Rolle, da diese Leerlaufzeit der CPU für andere Aufgaben genutzt wird und sich somit ideal zur Optimierung eignet.
Wenn Sie neugierig sind, etwas über Data Science zu lernen, schauen Sie sich das Executive PG Program in Data Science von IIIT-B & upGrad an, das für Berufstätige entwickelt wurde und mehr als 10 Fallstudien und Projekte, praktische Workshops, Mentoring mit Branchenexperten, 1 -on-1 mit Branchenmentoren, mehr als 400 Stunden Lern- und Jobunterstützung bei Top-Unternehmen.
Was ist ein Thread in Python?
Threads sind Entitäten innerhalb eines Prozesses, die zur Ausführung in Python geplant werden können. Laienhaft ausgedrückt ist ein Thread ein Rechenvorgang, der von einem Computer durchgeführt wird. Es ist ein Satz solcher Anweisungen innerhalb eines Programms, das Entwickler unabhängig von anderen Skripten ausführen können. Threads ermöglichen es Ihnen, die Anwendungsgeschwindigkeit durch die Verwendung von Parallelität zu erhöhen. Es ist ein leichtgewichtiger Prozess, der es ermöglicht, Aufgaben parallel auszuführen. Die Threads arbeiten unabhängig voneinander und maximieren die CPU-Auslastung, wodurch die CPU-Leistung verbessert wird.
Was ist die Verwendung von Multi-Thread in Python?
Multithreading ist eine Threading-Technik in der Python-Programmierung, die es vielen Threads ermöglicht, gleichzeitig zu arbeiten, indem schnell zwischen Threads mit Unterstützung einer CPU gewechselt wird (Kontextwechsel genannt). Wenn wir unsere Aufgabe in mehrere separate Abschnitte aufteilen können, verwenden wir Multithreading. Angenommen, Sie müssen eine komplexe Datenbankabfrage durchführen, um Daten abzurufen, und diese Abfrage in zahlreiche einzelne Abfragen aufteilen. In diesem Fall ist es vorzuziehen, jeder Abfrage einen Thread zuzuweisen und sie alle parallel auszuführen.
Was ist Thread-Synchronisation?
Thread-Synchronisation wird als eine Methode beschrieben, die garantiert, dass zwei oder mehr gleichzeitige Prozesse oder Threads einen entscheidenden Teil eines Programms nicht gleichzeitig ausführen. Synchronisationsmethoden werden verwendet, um den Zugriff von Prozessen auf wichtige Abschnitte zu steuern. Wenn wir zwei oder mehr Threads in einem Programm starten, besteht die Möglichkeit, dass mehrere Threads versuchen, auf dieselbe Ressource zuzugreifen, was aufgrund von Parallelitätsproblemen zu unerwarteten Ergebnissen führt. Wenn beispielsweise viele Threads versuchen, in dieselbe Datei zu schreiben, können die Daten beschädigt werden, weil einer der Threads Daten überschreiben kann oder wenn ein Thread dieselbe Datei öffnet und ein anderer Thread diese schließt.
