Multiprocessing in Python

Wie man in Python mehrere Aufgaben gleichzeitig erledigen kann...

    Inhaltsangabe
  1. Multiprocessing Process
    1. Ohne Multiprocessing Process
    2. Mit Multiprocessing Process
  2. Multiprocessing Pools
  3. __name__ == '__main__'
  4. Fazit

Ich hatte vor einiger Zeit angefangen, ein Python Script zu schreiben, um Dateien von Kunden-Servern herunterladen zu lassen, sodass ich ganze Kunden-Server sichern kann. Jedoch hatte ich das Problem, dass Python eine Synchrone Programmiersprache ist. Das bedeutet, dass jede Datei einzeln Heruntergeladen wird und währenddessen nichts anderes gemacht werden kann.

Wenn man bedenkt, dass wir hier von einem Ausmaß von mehr als einer Million Dateien sprechen, wird schnell deutlich, möglicherweise ein komplett-Backup bei einer Synchronen Programmierung mehr als 24 Stunden in Anspruch nehmen würde. Wir möchten jedoch dem Kunden ein Tägliches Backup zur Verfügung stellen. Dafür muss ein ganz neues Konzept her…

Multiprocessing Process

Mit Multiprocessing (Process) kann man dafür sorgen, dass eine Funktion im Python Script mehrfach aufgerufen wird. Das bedeutet, dass Python mehrere Prozesse generiert und in diesen Prozessen dieselbe Funktion mit unterschiedlichen Parametern ausführen lässt. Wenn wir den FTP Download als Beispiel nehmen, würde das so aussehen:

Ohne Multiprocessing Process

  • Wir wechseln in den Ordner
  • Listen uns alle Dateien auf
  • Datei 1 wird heruntergeladen
  • Es wird auf den Download gewartet
  • Datei 2 wird heruntergeladen
  • Es wird auf den Download gewartet
  • Datei 3 wird heruntergalden

Mit Multiprocessing Process

  • Wir wechseln in den Ordner
  • Listen uns alle Dateien auf
  • Wir übergeben Multiprocessing die Dateiliste, sodass alle Dateien heruntergeladen werden sollen.
  • Multiprocessing führt den Download zeitgleich für alle Dateien gleichzeitig aus, sodass während eine große Datei heruntergeladen wird, zeitgleich mehrere kleinere Dateien heruntergeladen werden.
import multiprocessing
import ftplib

project = {
    'slug'     : sys.argv[1],
    'server'   : sys.argv[2],
    'username' : sys.argv[3],
    'password' : sys.argv[4],
    'root'     : sys.argv[5],
}


def download(file):
    ftp_obj = ftplib.FTP(host=project['server'], user=project['username'], passwd=project['password'])
    

if __name__ == '__main__':
    for file in range(files):
        p = multiprocessing.Process(target=download, args=(file,))
        jobs.append(p)
        p.start()

Wie wir sehen, erhöht Multiprocessing unsere Produktivität enorm. Ein Problem hatte ich jedoch. Wenn ich zeitgleich alle Dateien an Multiprocessing übergebe, kommt es bei den meisten Webhostern zu einer Sperrung, weil meistens maximal 10 Verbindungen gleichzeitig erlaubt sind. Was tun?

Multiprocessing Pools

Multiprocessing liefert eine Klasse Pool mit, welches genau das macht, was ihr euch schon denken könnt. Es dient als eine Art Transistor für Prozesse.

Kommen wir zurück zu unserem FTP Download Problem. Man hat sagen wir mal einen Array mit 10.000 Dateien, darf aber jedoch nur maximal 10 Verbindungen zum FTP Server aufbauen. Dann erstellt man dank Pools einfach eine bestimmte Anzahl an Prozessen. Z.B. Fünf

from multiprocessing import Pool
Pool = Pool(processes=5)

Danach kann man mit der Klasse map des Pool Modules exakt das selbe machen, was oben beschrieben wurde, nur dass maximal fünf Prozesse gleichzeitig laufen. Also das würde dann etwa so aussehen:

from multiprocessing import Pool
import ftplib

project = {
    'slug'     : sys.argv[1],
    'server'   : sys.argv[2],
    'username' : sys.argv[3],
    'password' : sys.argv[4],
    'root'     : sys.argv[5],
}

def download(file):
    ftp_obj = ftplib.FTP(host=project['server'], user=project['username'], passwd=project['password'])
    

if __name__ == '__main__':
    Pool = Pool(processes=5)
    Pool.map_async(download, files)
    Pool.close()
    Pool.join()

__name__ == ‚__main__‘

Falls ihr in den Code-Zeilen oben bereits gemerkt habt, habe ich den Hauptteil meines Programmes immer mit einem if __name__ == ‚__main__‘: angefangen. Das bedeutet, dass alles was sich innerhalb von dieser if Verzweigung befindet, nur bei der Hauptausführung ausgeführt wird.

Nochmal einfacher erklärt: Wenn man Multiprocessing benutzt, wird dieser Python Script immer wieder ausgeführt. Wenn man nicht verhindert, dass die Hauptfunktionen bei jedem Aufruf ausgeführt werden, wird man in einem Loop enden. Das bedeutet logischerweise, dass das Programm nie enden wird, weil jedes mal das ganze Programm noch einmal neu gestartet wird.

Fazit

Natürlich bringt Multiprocessing eine Menge unterschiedliche und sehr umfangreiche Klassen mit die man nutzen kann. Ich kann jedoch nicht alle hier auflisten, sondern habe die Klassen euch näher gebracht, die ich in meinem Backup Script verwendet habe. Weitere Informationen zur Multiprocessing könnt ihr hier finden.

Darüber hinaus könnt ihr mir auch gerne eine Nachricht per Mail an info@talhasariyuerek.com schreiben. Ich werde euch helfen so gut ich kann, jedoch solltet ihr wissen, dass ich auch noch in der Lernphase von Python bin.

Themen

Produktivität Programmieren Python Technik

Beitrag teilen

WhatsAppen

Folgen Sie uns