Python Parallelverarbeitung: Multithreading, Multiprocessing & Asyncio erklärt

1. Einführung

Die Bedeutung der Parallelverarbeitung in Python

Python wird aufgrund seiner Einfachheit und Benutzerfreundlichkeit in einer Vielzahl von Anwendungsbereichen eingesetzt. Bei komplexen Datenverarbeitungen oder rechenintensiven Aufgaben kann die Ausführungsgeschwindigkeit von Python jedoch zu einer Herausforderung werden. Um dieses Problem zu lösen, spielt die „Parallelverarbeitung“, bei der mehrere Aufgaben gleichzeitig ausgeführt werden, eine entscheidende Rolle. In diesem Artikel wird erklärt, wie man Parallelverarbeitung in Python implementieren kann – von grundlegenden Methoden bis hin zu konkreten Anwendungsfällen.

2. Methoden der Parallelverarbeitung in Python

Wichtige Ansätze für Parallelverarbeitung

Python bietet mehrere Möglichkeiten zur Umsetzung von Parallelverarbeitung. Die drei wichtigsten sind:

  1. Multithreading (threading Modul)
    Aufgaben werden mit mehreren Threads parallel ausgeführt. Aufgrund des GIL (Global Interpreter Lock) ist der Nutzen jedoch bei CPU-intensiven Prozessen eingeschränkt.
  2. Multiprocessing (multiprocessing Modul)
    Jeder Prozess besitzt einen eigenen Speicherbereich und ist nicht vom GIL betroffen. Dadurch ist echte Parallelität möglich, was sich besonders für rechenintensive Aufgaben und große Datenmengen eignet.
  3. Asynchrones Programmieren (asyncio Modul)
    Besonders effektiv bei I/O-lastigen Aufgaben (z. B. Netzwerk- oder Dateiverarbeitung). Dadurch können Prozesse mit hoher Wartezeit effizient abgearbeitet werden.
侍エンジニア塾

3. Multiprocessing vs. Multithreading

Die Rolle des GIL (Global Interpreter Lock)

Python verwendet den sogenannten GIL, der verhindert, dass mehrere Threads gleichzeitig ausgeführt werden. Dies limitiert die Leistungssteigerung bei CPU-intensiven Prozessen, selbst wenn mehrere Threads genutzt werden. Daher ist Multithreading hauptsächlich für I/O-lastige Aufgaben sinnvoll.

Vorteile und Einschränkungen von Multithreading

Threads sind leichtgewichtig und eignen sich gut für I/O-lastige Aufgaben (z. B. Dateioperationen, Netzwerkkommunikation). Aufgrund des GIL ist Multithreading jedoch für CPU-lastige Aufgaben ungeeignet, da nicht alle CPU-Kerne optimal genutzt werden können.

„`
import threading
import time

def worker(num):
print(f“Worker {num} starting“)
time.sleep(2)
print(f“Worker {num} finished“)

threads = [] for i in range(5):
t = threading.Thread(target=worker, args=(i,))
threads.append(t)
t.start()

for t in threads:
t.join()
„`

Dieser Code startet 5 Threads gleichzeitig, die jeweils 2 Sekunden pausieren, bevor sie beendet werden. So lässt sich beobachten, wie Multithreading parallele Ausführung ermöglicht.

Vorteile von Multiprocessing

Multiprocessing umgeht die GIL-Einschränkung. Prozesse laufen in unabhängigen Speicherbereichen und können mehrere CPU-Kerne voll ausnutzen. Besonders bei großen Datenmengen oder rechenintensiven Aufgaben bringt dies deutliche Vorteile.

„`
from multiprocessing import Process
import time

def worker(num):
print(f“Worker {num} starting“)
time.sleep(2)
print(f“Worker {num} finished“)

if __name__ == ‚__main__‘:
processes = [] for i in range(5):
p = Process(target=worker, args=(i,))
processes.append(p)
p.start()

for p in processes:
p.join()
„`

In diesem Beispiel werden 5 Prozesse parallel ausgeführt. Jeder arbeitet unabhängig, und join() stellt sicher, dass das Programm wartet, bis alle Prozesse beendet sind.

4. Implementierung von Parallelverarbeitung in Python

Parallelisierung mit dem multiprocessing-Modul

Das multiprocessing-Modul ermöglicht eine effiziente Verwaltung mehrerer Prozesse. Beispiel mit einem Prozesspool:

„`
from multiprocessing import Pool

def square(x):
return x * x

if __name__ == ‚__main__‘:
with Pool(4) as p:
result = p.map(square, [1, 2, 3, 4, 5])
print(result)
„`

Hier führen 4 Prozesse gleichzeitig Quadratberechnungen durch und geben die Ergebnisse als Liste zurück.

侍エンジニア塾

5. Asynchrone Verarbeitung und ihre Anwendungen

Asynchrone Verarbeitung mit asyncio

Das asyncio-Modul ist ideal für I/O-lastige Aufgaben wie Netzwerkeingaben/-ausgaben oder Dateizugriffe. Während Wartezeiten können andere Aufgaben parallel ausgeführt werden.

„`
import asyncio

async def worker(num):
print(f’Worker {num} starting‘)
await asyncio.sleep(1)
print(f’Worker {num} finished‘)

async def main():
tasks = [worker(i) for i in range(5)] await asyncio.gather(*tasks)

asyncio.run(main())
„`

Hier laufen 5 Aufgaben parallel, wobei await sicherstellt, dass während der Wartezeit andere Tasks ausgeführt werden können.

6. Performance-Tuning für Parallelverarbeitung

Parallelisierung mit Joblib

Joblib ist besonders nützlich für datenintensive Berechnungen oder das Training von Machine-Learning-Modellen. Beispiel:

„`
from joblib import Parallel, delayed

def heavy_task(n):
return n ** 2

results = Parallel(n_jobs=4)(delayed(heavy_task)(i) for i in range(10))
print(results)
„`

Mit n_jobs lässt sich die Anzahl paralleler Prozesse steuern. Hier werden 4 Prozesse genutzt, die die Berechnungen beschleunigen.

7. Praktische Anwendungen der Parallelverarbeitung in Python

Datenverarbeitung und Web-Scraping

Parallelverarbeitung ist besonders nützlich bei Aufgaben wie Datenanalyse und Web-Scraping. Beispielsweise können durch Multithreading oder asynchrone Verarbeitung mehrere Webanfragen gleichzeitig ausgeführt werden, wodurch sich die Verarbeitungszeit erheblich verkürzt. Auch beim Training von Machine-Learning-Modellen oder bei der Datenvorverarbeitung helfen multiprocessing und Joblib, die Performance deutlich zu steigern.

8. Fazit

Parallelverarbeitung ist unverzichtbar, um die Leistungsfähigkeit von Python voll auszuschöpfen. Mit Modulen wie threading, multiprocessing, asyncio und Joblib lassen sich Aufgaben in unterschiedlichsten Szenarien effizient abwickeln. Nutzen Sie diese Techniken in Ihren Projekten, um die Performance zu optimieren.

年収訴求