Parallelität in Python - Multiprocessing
In diesem Kapitel konzentrieren wir uns mehr auf den Vergleich zwischen Multiprocessing und Multithreading.
Mehrfachverarbeitung
Hierbei werden zwei oder mehr CPU-Einheiten in einem einzelnen Computersystem verwendet. Dies ist der beste Ansatz, um das volle Potenzial unserer Hardware auszuschöpfen, indem Sie die gesamte Anzahl der in unserem Computersystem verfügbaren CPU-Kerne nutzen.
Multithreading
Es ist die Fähigkeit einer CPU, die Verwendung des Betriebssystems durch gleichzeitiges Ausführen mehrerer Threads zu verwalten. Die Hauptidee von Multithreading besteht darin, Parallelität zu erreichen, indem ein Prozess in mehrere Threads aufgeteilt wird.
Die folgende Tabelle zeigt einige der wichtigen Unterschiede zwischen ihnen -
Mehrfachverarbeitung | Multiprogrammierung |
---|---|
Multiprocessing bezieht sich auf die Verarbeitung mehrerer Prozesse gleichzeitig durch mehrere CPUs. | Durch die Multiprogrammierung werden mehrere Programme gleichzeitig im Hauptspeicher gespeichert und gleichzeitig mit einer einzigen CPU ausgeführt. |
Es werden mehrere CPUs verwendet. | Es verwendet eine einzelne CPU. |
Es ermöglicht eine parallele Verarbeitung. | Es findet eine Kontextumschaltung statt. |
Weniger Zeit für die Bearbeitung der Aufträge. | Mehr Zeit für die Bearbeitung der Aufträge. |
Es erleichtert eine sehr effiziente Nutzung von Geräten des Computersystems. | Weniger effizient als Multiprocessing. |
Normalerweise teurer. | Solche Systeme sind kostengünstiger. |
Beseitigung der Auswirkungen der globalen Dolmetschersperre (GIL)
Bei der Arbeit mit gleichzeitigen Anwendungen gibt es in Python eine Einschränkung namens GIL (Global Interpreter Lock). GIL erlaubt uns niemals, mehrere CPU-Kerne zu verwenden, und daher können wir sagen, dass es in Python keine echten Threads gibt. GIL ist die Mutex-Sperre für gegenseitigen Ausschluss, die die Sicherheit von Threads gewährleistet. Mit anderen Worten, wir können sagen, dass GIL verhindert, dass mehrere Threads Python-Code parallel ausführen. Die Sperre kann jeweils nur von einem Thread gehalten werden. Wenn ein Thread ausgeführt werden soll, muss er zuerst die Sperre erwerben.
Mit der Verwendung von Multiprocessing können wir die durch GIL verursachte Einschränkung effektiv umgehen -
Durch die Verwendung von Multiprocessing nutzen wir die Fähigkeit mehrerer Prozesse und daher mehrere Instanzen der GIL.
Aus diesem Grund gibt es keine Einschränkung, den Bytecode eines Threads innerhalb unserer Programme gleichzeitig auszuführen.
Prozesse in Python starten
Die folgenden drei Methoden können verwendet werden, um einen Prozess in Python innerhalb des Multiprocessing-Moduls zu starten:
- Fork
- Spawn
- Forkserver
Erstellen eines Prozesses mit Fork
Der Fork-Befehl ist ein Standardbefehl unter UNIX. Es wird verwendet, um neue Prozesse zu erstellen, die als untergeordnete Prozesse bezeichnet werden. Dieser untergeordnete Prozess wird gleichzeitig mit dem als übergeordneter Prozess bezeichneten Prozess ausgeführt. Diese untergeordneten Prozesse sind auch mit ihren übergeordneten Prozessen identisch und erben alle Ressourcen, die dem übergeordneten Prozess zur Verfügung stehen. Die folgenden Systemaufrufe werden beim Erstellen eines Prozesses mit Fork verwendet:
fork()- Es handelt sich um einen Systemaufruf, der im Allgemeinen im Kernel implementiert ist. Es wird verwendet, um eine Kopie des Prozesses zu erstellen
getpid() - Dieser Systemaufruf gibt die Prozess-ID (PID) des aufrufenden Prozesses zurück.
Beispiel
Das folgende Python-Skriptbeispiel hilft Ihnen dabei, zu verstehen, wie Sie einen neuen untergeordneten Prozess erstellen und die PIDs von untergeordneten und übergeordneten Prozessen abrufen.
import os
def child():
n = os.fork()
if n > 0:
print("PID of Parent process is : ", os.getpid())
else:
print("PID of Child process is : ", os.getpid())
child()
Ausgabe
PID of Parent process is : 25989
PID of Child process is : 25990
Erstellen eines Prozesses mit Spawn
Spawn bedeutet, etwas Neues zu beginnen. Das Laichen eines Prozesses bedeutet daher die Erstellung eines neuen Prozesses durch einen übergeordneten Prozess. Der übergeordnete Prozess setzt seine Ausführung asynchron fort oder wartet, bis der untergeordnete Prozess seine Ausführung beendet. Befolgen Sie diese Schritte, um einen Prozess zu erzeugen -
Multiprozessor-Modul importieren.
Objektprozess erstellen.
Starten der Prozessaktivität durch Aufrufen start() Methode.
Warten Sie, bis der Prozess seine Arbeit beendet hat, und beenden Sie ihn durch einen Anruf join() Methode.
Beispiel
Das folgende Beispiel eines Python-Skripts hilft beim Erstellen von drei Prozessen
import multiprocessing
def spawn_process(i):
print ('This is process: %s' %i)
return
if __name__ == '__main__':
Process_jobs = []
for i in range(3):
p = multiprocessing.Process(target = spawn_process, args = (i,))
Process_jobs.append(p)
p.start()
p.join()
Ausgabe
This is process: 0
This is process: 1
This is process: 2
Erstellen eines Prozesses mit Forkserver
Der Forkserver-Mechanismus ist nur auf den ausgewählten UNIX-Plattformen verfügbar, die die Übergabe der Dateideskriptoren über Unix Pipes unterstützen. Beachten Sie die folgenden Punkte, um die Funktionsweise des Forkserver-Mechanismus zu verstehen:
Ein Server wird instanziiert, wenn der Forkserver-Mechanismus zum Starten eines neuen Prozesses verwendet wird.
Der Server empfängt dann den Befehl und verarbeitet alle Anforderungen zum Erstellen neuer Prozesse.
Um einen neuen Prozess zu erstellen, sendet unser Python-Programm eine Anfrage an Forkserver und erstellt einen Prozess für uns.
Endlich können wir diesen neu erstellten Prozess in unseren Programmen verwenden.
Daemon-Prozesse in Python
Python multiprocessingModul ermöglicht es uns, Daemon-Prozesse durch seine Daemonic-Option zu haben. Daemon-Prozesse oder die Prozesse, die im Hintergrund ausgeführt werden, folgen einem ähnlichen Konzept wie die Daemon-Threads. Um den Prozess im Hintergrund auszuführen, müssen wir das Daemonic-Flag auf true setzen. Der Daemon-Prozess wird so lange ausgeführt, wie der Hauptprozess ausgeführt wird, und er wird beendet, nachdem die Ausführung abgeschlossen wurde oder wenn das Hauptprogramm beendet wird.
Beispiel
Hier verwenden wir dasselbe Beispiel wie in den Daemon-Threads. Der einzige Unterschied ist der Wechsel des Moduls vonmultithreading zu multiprocessingund Setzen des dämonischen Flags auf true. Es würde jedoch eine Änderung der Ausgabe geben, wie unten gezeigt -
import multiprocessing
import time
def nondaemonProcess():
print("starting my Process")
time.sleep(8)
print("ending my Process")
def daemonProcess():
while True:
print("Hello")
time.sleep(2)
if __name__ == '__main__':
nondaemonProcess = multiprocessing.Process(target = nondaemonProcess)
daemonProcess = multiprocessing.Process(target = daemonProcess)
daemonProcess.daemon = True
nondaemonProcess.daemon = False
daemonProcess.start()
nondaemonProcess.start()
Ausgabe
starting my Process
ending my Process
Die Ausgabe unterscheidet sich von der von Daemon-Threads generierten, da der Prozess in keinem Daemon-Modus eine Ausgabe hat. Daher endet der dämonische Prozess automatisch nach dem Ende der Hauptprogramme, um das Fortbestehen laufender Prozesse zu vermeiden.
Prozesse in Python beenden
Wir können einen Prozess sofort beenden oder beenden, indem wir das verwenden terminate()Methode. Wir werden diese Methode verwenden, um den mit Hilfe der Funktion erstellten untergeordneten Prozess unmittelbar vor Abschluss seiner Ausführung zu beenden.
Beispiel
import multiprocessing
import time
def Child_process():
print ('Starting function')
time.sleep(5)
print ('Finished function')
P = multiprocessing.Process(target = Child_process)
P.start()
print("My Process has terminated, terminating main thread")
print("Terminating Child Process")
P.terminate()
print("Child Process successfully terminated")
Ausgabe
My Process has terminated, terminating main thread
Terminating Child Process
Child Process successfully terminated
Die Ausgabe zeigt, dass das Programm vor der Ausführung des untergeordneten Prozesses beendet wird, der mit Hilfe der Funktion Child_process () erstellt wurde. Dies bedeutet, dass der untergeordnete Prozess erfolgreich beendet wurde.
Identifizieren des aktuellen Prozesses in Python
Jeder Prozess im Betriebssystem hat eine Prozessidentität, die als PID bezeichnet wird. In Python können wir die PID des aktuellen Prozesses mithilfe des folgenden Befehls ermitteln:
import multiprocessing
print(multiprocessing.current_process().pid)
Beispiel
Das folgende Beispiel eines Python-Skripts hilft dabei, die PID des Hauptprozesses sowie die PID des untergeordneten Prozesses herauszufinden.
import multiprocessing
import time
def Child_process():
print("PID of Child Process is: {}".format(multiprocessing.current_process().pid))
print("PID of Main process is: {}".format(multiprocessing.current_process().pid))
P = multiprocessing.Process(target=Child_process)
P.start()
P.join()
Ausgabe
PID of Main process is: 9401
PID of Child Process is: 9402
Verwenden eines Prozesses in der Unterklasse
Wir können Threads erstellen, indem wir das unterordnen threading.ThreadKlasse. Darüber hinaus können wir auch Prozesse erstellen, indem wir die unterklassifizierenmultiprocessing.ProcessKlasse. Um einen Prozess in einer Unterklasse zu verwenden, müssen wir die folgenden Punkte berücksichtigen:
Wir müssen eine neue Unterklasse der definieren Process Klasse.
Wir müssen das außer Kraft setzen _init_(self [,args] ) Klasse.
Wir müssen das von außer Kraft setzen run(self [,args] ) Methode, um was zu implementieren Process
Wir müssen den Prozess durch Aufrufen von startenstart() Methode.
Beispiel
import multiprocessing
class MyProcess(multiprocessing.Process):
def run(self):
print ('called run method in process: %s' %self.name)
return
if __name__ == '__main__':
jobs = []
for i in range(5):
P = MyProcess()
jobs.append(P)
P.start()
P.join()
Ausgabe
called run method in process: MyProcess-1
called run method in process: MyProcess-2
called run method in process: MyProcess-3
called run method in process: MyProcess-4
called run method in process: MyProcess-5
Python Multiprocessing Module - Poolklasse
Wenn wir über einfache Parallele sprechen processingAufgaben in unseren Python-Anwendungen und dann das Multiprocessing-Modul stellen uns die Pool-Klasse zur Verfügung. Die folgenden Methoden vonPool class kann verwendet werden, um die Anzahl der untergeordneten Prozesse in unserem Hauptprogramm zu erhöhen
apply () -Methode
Diese Methode ähnelt der.submit()Methode von .ThreadPoolExecutor.Es wird blockiert, bis das Ergebnis fertig ist.
apply_async () -Methode
Wenn wir unsere Aufgaben parallel ausführen müssen, müssen wir die verwendenapply_async()Methode zum Senden von Aufgaben an den Pool. Es ist eine asynchrone Operation, die den Hauptthread erst sperrt, wenn alle untergeordneten Prozesse ausgeführt wurden.
map () Methode
Genau wie die apply()Methode blockiert es auch, bis das Ergebnis fertig ist. Es entspricht dem eingebautenmap() Funktion, die die iterierbaren Daten in mehrere Blöcke aufteilt und als separate Aufgaben an den Prozesspool übermittelt.
map_async () -Methode
Es ist eine Variante des map() Methode als apply_async() ist zum apply()Methode. Es gibt ein Ergebnisobjekt zurück. Wenn das Ergebnis fertig ist, wird ein Callable darauf angewendet. Der Anruf muss sofort abgeschlossen sein; Andernfalls wird der Thread, der die Ergebnisse verarbeitet, blockiert.
Beispiel
Das folgende Beispiel hilft Ihnen bei der Implementierung eines Prozesspools für die parallele Ausführung. Eine einfache Berechnung des Quadrats der Zahl wurde durchgeführt, indem das angewendet wurdesquare() Funktion durch die multiprocessing.PoolMethode. Dannpool.map() wurde verwendet, um die 5 zu senden, da die Eingabe eine Liste von Ganzzahlen von 0 bis 4 ist. Das Ergebnis würde in gespeichert p_outputs und es wird gedruckt.
def square(n):
result = n*n
return result
if __name__ == '__main__':
inputs = list(range(5))
p = multiprocessing.Pool(processes = 4)
p_outputs = pool.map(function_square, inputs)
p.close()
p.join()
print ('Pool :', p_outputs)
Ausgabe
Pool : [0, 1, 4, 9, 16]