Python Multiprocessing: Vollständiger Leitfaden | Nutzung, Optimierung, Fehlerbehandlung

目次

1. Grundlagen: Was ist Multiprocessing in Python?

1.1 Was ist Multiprocessing?

Multiprocessing bedeutet, mehrere Prozesse (unabhängige Ausführungseinheiten) gleichzeitig auszuführen. In Python kann Multiprocessing mit dem multiprocessing-Modul einfach implementiert werden.

Merkmale des Multiprocessings

  • Jeder Prozess hat einen unabhängigen Speicherraum
  • CPU-Kerne können maximal genutzt werden
  • Kommunikation zwischen Prozessen ist notwendig (Queue oder Pipe verwenden)

Konkrete Anwendungsszenarien

  • Massenhaft rechenintensive Verarbeitung (Maschinelles Lernen, numerische Simulation)
  • Aufgaben, die die CPU voll ausnutzen (Bildverarbeitung, Datenanalyse)

1.2 Unterschiede zu Multithreading

In Python gibt es auch ein paralleles Verarbeitungssystem namens „Multithreading“.Wie unterscheiden sich Multiprocessing und Multithreading?

KriteriumMultiprocessingMultithreading
SpeicherfreigabeNein (unabhängige Prozesse)Ja (innerhalb desselben Prozesses)
Einfluss des GILKein EinflussBetroffen
Geeignet für CPU-bound
Geeignet für I/O-bound
DatenübertragungQueue oder Pipe erforderlichShared Memory kann verwendet werden

Was ist GIL (Global Interpreter Lock)?

Der Standard-Interpreter von Python (CPython) hat ein Mechanismus namens „GIL“, durch den auch bei Multithreading nur ein Thread gleichzeitig ausgeführt werden kann. Daher ist Multiprocessing geeignet, wenn die CPU voll ausgenutzt werden soll.

1.3 Einfaches Implementierungsbeispiel für Multiprocessing in Python

import multiprocessing
 import time
 
 def worker(n):
     print(f"Prozess {n} gestartet")
     time.sleep(2)
     print(f"Prozess {n} beendet")
 
 if __name__ == "__main__":
     process_list = []
 
     # 3 Prozesse erstellen
     for i in range(3):
         p = multiprocessing.Process(target=worker, args=(i,))
         process_list.append(p)
         p.start()
 
     # Auf Beendigung aller Prozesse warten
     for p in process_list:
         p.join()
 
     print("Alle Prozesse beendet")

1.4 Vorsichtsmaßnahmen beim Einsatz von Multiprocessing

1. Unter Windows ist if __name__ == "__main__": erforderlich

In der Windows-Umgebung tritt beim Einsatz von multiprocessing.Process() ein Fehler auf, wenn if __name__ == "__main__": nicht geschrieben wird.

Falscher Code (verursacht Fehler)
import multiprocessing
 
 def worker():
     print("Hello from process")
 
 p = multiprocessing.Process(target=worker)
 p.start()

Dieser Code verursacht unter Windows einen Fehler.

Richtiger Code
import multiprocessing
 
 def worker():
     print("Hello from process")
 
 if __name__ == "__main__":
     p = multiprocessing.Process(target=worker)
     p.start()
     p.join()

Durch das Hinzufügen von if __name__ == "__main__": funktioniert es auch unter Windows korrekt.

1.5 Zusammenfassung

  • Was ist Multiprocessing?Technik, um mehrere Prozesse parallel auszuführen
  • Unterschiede zu MultithreadingNicht beeinflusst vom GIL, geeignet für CPU-bound-Aufgaben
  • Einfaches Implementierungsbeispiel in Python → Verwenden von multiprocessing.Process()
  • Vorsichtsmaßnahmen unter Windowsif __name__ == "__main__": ist notwendig

2. Praktischer Teil: Verwendung des multiprocessing-Moduls

2.1 Überblick über das multiprocessing-Modul

multiprocessingModul ist eine Standardbibliothek für die Durchführung von prozessbasierter paralleler Verarbeitung in Python.
Mit diesem Modul können Sie die CPU-Kerne voll ausnutzen und die Einschränkungen des GIL umgehen.

Hauptfunktionen von multiprocessing

FunktionBeschreibung
ProcessEinzelnen Prozess erstellen und ausführen
QueueDaten zwischen Prozessen senden und empfangen
PipeDaten zwischen zwei Prozessen austauschen
Value & ArrayGeteilten Speicher zwischen Prozessen nutzen
PoolEinen Pool von Prozessen erstellen und effizient parallele Verarbeitung durchführen

2.2 Grundlegende Verwendung der Process-Klasse

Um in Python einen neuen Prozess zu erstellen, verwenden Sie die multiprocessing.Process-Klasse.

Grundlegende Prozesserstellung

import multiprocessing
 import time
 
 def worker(n):
     print(f"Prozess {n} gestartet")
     time.sleep(2)
     print(f"Prozess {n} beendet")
 
 if __name__ == "__main__":
     p1 = multiprocessing.Process(target=worker, args=(1,))
     p2 = multiprocessing.Process(target=worker, args=(2,))
 
     p1.start()
     p2.start()
 
     p1.join()
     p2.join()
 
     print("Alle Prozesse beendet")

2.3 Prozessinterne Kommunikation (Queue & Pipe)

Datensendung und -empfang mit Queue

import multiprocessing
 
 def worker(q):
     q.put("Hallo vom Kindprozess")
 
 if __name__ == "__main__":
     q = multiprocessing.Queue()
     p = multiprocessing.Process(target=worker, args=(q,))
     p.start()
     p.join()
 
     # Daten vom Kindprozess abrufen
     print(q.get())

2.4 Geteilter Speicher mit Value und Array

import multiprocessing
 
 def worker(val, arr):
     val.value = 3.14  # Wert des geteilten Speichers ändern
     arr[0] = 42       # Wert des Arrays ändern
 
 if __name__ == "__main__":
     val = multiprocessing.Value('d', 0.0)  # 'd' ist double-Typ
     arr = multiprocessing.Array('i', [0, 1, 2])  # 'i' ist Integer-Typ
 
     p = multiprocessing.Process(target=worker, args=(val, arr))
     p.start()
     p.join()
 
     print(f"val: {val.value}, arr: {arr[:]}")

2.5 Prozessverwaltung mit der Pool-Klasse

Parallele Verarbeitung mit Pool

import multiprocessing
 
 def square(n):
     return n * n
 
 if __name__ == "__main__":
     with multiprocessing.Pool(4) as pool:
         results = pool.map(square, range(10))
 
     print(results)

2.6 Zusammenfassung

  • Mit dem multiprocessing-Modul kann parallele Verarbeitung einfach implementiert werden
  • Mit der Process-Klasse einzelne Prozesse erstellen
  • Mit Queue oder Pipe können Daten zwischen Prozessen geteilt werden
  • Mit Value oder Array Speicher teilen
  • Mit der Pool-Klasse große Datenmengen effizient verarbeiten

3. Fortgeschrittenes: Fehlerbehandlung und Leistungsoptimierung

3.1 Häufige Fehler und Maßnahmen in multiprocessing

Fehler 1: Fehlender if __name__ == "__main__":-Block im Windows-Umfeld

Fehlermeldung
RuntimeError: freeze_support() must be called if program is run in frozen mode
Lösung
import multiprocessing

 def worker():
    print("Hello from process")

if __name__ == "__main__":  # Dies ist notwendig
    p = multiprocessing.Process(target=worker)
    p.start()
    p.join()

Fehler 2: PicklingError (Funktion kann nicht zwischen Prozessen übergeben werden)

Fehlermeldung
AttributeError: Can't pickle local object 'main.<locals>.<lambda>'
Lösung
import multiprocessing

def square(x):  # Als globale Funktion definieren
    return x * x

if __name__ == "__main__":
    with multiprocessing.Pool(4) as pool:
        results = pool.map(square, range(10))  # Lambda vermeiden
    print(results)

Fehler 3: Deadlock (Prozess bleibt hängen)

Lösung
import multiprocessing

def worker(q):
    q.put("data")

if __name__ == "__main__":
    q = multiprocessing.Queue()
    p = multiprocessing.Process(target=worker, args=(q,))
    p.start()
    print(q.get())  # Daten empfangen
    p.join()  # Hier normal beenden

3.2 Techniken zur Leistungsoptimierung

Optimierung 1: Anzahl der Prozesse angemessen einstellen

import multiprocessing

def worker(n):
    return n * n

if __name__ == "__main__":
    num_workers = multiprocessing.cpu_count()  # Anzahl der CPU-Kerne abrufen
    with multiprocessing.Pool(num_workers) as pool:
        results = pool.map(worker, range(100))
    print(results)

Optimierung 2: Pool.starmap() verwenden

import multiprocessing

def multiply(a, b):
    return a * b

if __name__ == "__main__":
    with multiprocessing.Pool(4) as pool:
        results = pool.starmap(multiply, [(1, 2), (3, 4), (5, 6)])
    print(results)

Optimierung 3: Gemeinsamen Speicher nutzen

import multiprocessing
import ctypes

def worker(shared_array):
    shared_array[0] = 99  # Wert im gemeinsamen Speicher ändern

if __name__ == "__main__":
    shared_array = multiprocessing.Array(ctypes.c_int, [1, 2, 3])  # Gemeinsamen Speicher erstellen
    p = multiprocessing.Process(target=worker, args=(shared_array,))
    p.start()
    p.join()
    print(shared_array[:])  # [99, 2, 3]

3.3 Zusammenfassung

  • multiprocessing für häufige Fehlervermeidungsmethoden erläutert
  • Leistungsoptimierung-Punkte:
  • Anzahl der Prozesse angemessen einstellen
  • starmap() nutzen
  • Mit gemeinsamem Speicher beschleunigen

4. FAQ: Häufige Fragen und Lösungen

4.1 Multiprocessing und Multithreading in Python: Welches sollte man verwenden?

Antwort

  • CPU-gebunden (Prozesse mit hoher Rechenlast)Multiprocessing (multiprocessing)
  • I/O-gebunden (Datei- und Netzwerkverarbeitung)Multithreading (threading)
Art der VerarbeitungGeeignete Parallelverarbeitung
CPU-gebunden (numerische Berechnungen, Bildverarbeitung usw.)Multiprocessing (multiprocessing)
I/O-gebunden (Dateien, API-Anfragen usw.)Multithreading (threading)

4.2 Warum fühlt sich multiprocessing „langsam“ an?

Antwort

  • Hohe Kosten für die ProzesserstellungPool verwenden
  • Zu viele Datenkopien → Gemeinsamen Speicher (Value, Array) verwenden
  • Viele kleine Aufgaben verarbeitenconcurrent.futures.ThreadPoolExecutor ausprobieren
import multiprocessing

 def worker(n):
    return n * n

if __name__ == "__main__":
    with multiprocessing.Pool(multiprocessing.cpu_count()) as pool:
        results = pool.map(worker, range(100))
    print(results)

4.3 Wie teilt man Wörterbücher oder Listen in Multiprocessing?

Antwort

multiprocessing.Manager() verwenden

import multiprocessing

 def worker(shared_list):
    shared_list.append(100)  # Gemeinsame Liste aktualisieren

if __name__ == "__main__":
    with multiprocessing.Manager() as manager:
        shared_list = manager.list([1, 2, 3])
        p = multiprocessing.Process(target=worker, args=(shared_list,))
        p.start()
        p.join()
        print(shared_list)  # [1, 2, 3, 100]

4.4 Häufige Fehler bei multiprocessing.Pool und deren Behebung?

FehlerUrsacheLösung
AttributeError: Can't pickle local objectlambda oder lokale Funktionen übergebenGlobale Funktionen verwenden
RuntimeError: freeze_support() must be calledWindows-Umgebung ohne if __name__ == "__main__":if __name__ == "__main__": hinzufügen
EOFError: Ran out of inputPool, Prozesse wurden nicht normal beendetpool.close() und pool.join() ordnungsgemäß aufrufen

4.5 Wie debuggt man Multiprocessing in Python?

Antwort

multiprocessing.log_to_stderr() nutzen

import multiprocessing
 import logging

 def worker(n):
    logger = multiprocessing.get_logger()
    logger.info(f"Prozess {n} wird ausgeführt")

if __name__ == "__main__":
    multiprocessing.log_to_stderr(logging.INFO)  # Logging aktivieren
    p = multiprocessing.Process(target=worker, args=(1,))
    p.start()
    p.join()
RUNTEQ(ランテック)|超実戦型エンジニア育成スクール

5. Zusammenfassung und zusätzliche Lernressourcen

5.1 Zusammenfassung dieses Artikels

Grundlagen des Multiprocessing

  • Was ist Multiprocessing?Technik, um mehrere Prozesse parallel auszuführen und die CPU optimal zu nutzen
  • Unterschiede zum Multithreading
  • Multiprocessing → Geeignet für CPU-gebundene Aufgaben (numerische Berechnungen, Bildverarbeitung usw.)
  • Multithreading → Geeignet für I/O-gebundene Aufgaben (Dateiverarbeitung, Netzwerkkommunikation usw.)

multiprocessing Modul-Verwendung

  • Mit der Process-Klasse einzelne Prozesse erstellen
  • Mit Queue oder Pipe Daten zwischen Prozessen senden und empfangen
  • Value oder Array nutzen, um gemeinsamen Speicher zu verwenden
  • Mit der Pool-Klasse effiziente parallele Verarbeitung durchführen

Fehlerbehandlung und Performance-Optimierung

  • Häufige Fehler
  • if __name__ == "__main__": nicht schreiben führt zu Fehlern unter Windows
  • lambda-Funktionen oder lokale Funktionen verursachen PicklingError
  • Queue.get() vergessen führt zu Deadlocks
  • Performance-Optimierung
  • Pool nutzen, um die Kosten der Prozesserstellung zu reduzieren
  • starmap() verwenden, um mehrere Argumente zu übergeben
  • multiprocessing.shared_memory nutzen, um den Overhead durch Datenkopien zu reduzieren

5.2 Zusätzliche Lernressourcen

1. Offizielle Python-Dokumentation

2. Online-Tutorials

5.3 Für die zukünftige Nutzung

Durch die angemessene Nutzung von Pythons multiprocessing kann die CPU effizient genutzt werden, um leistungsstarke Programme zu erstellen.

Als Nächstes zu lernende Techniken

  • Asynchrone Verarbeitung (asyncio) → Parallelisierung von I/O-gebundenen Aufgaben
  • concurrent.futures → Integriertes Management von Threads und Prozessen

5.4 Zum Abschluss

In diesem Artikel haben wir „Python Multiprocessing“ von den Grundlagen über die Praxis bis hin zu Anwendungen detailliert erläutert. Nutzen Sie das in diesem Artikel erlernte Wissen und wenden Sie es in realen Projekten an! 🚀

RUNTEQ(ランテック)|超実戦型エンジニア育成スクール