[Multiprocessing] komisches Verhalten

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
Antworten
TenchiMuyo1984
User
Beiträge: 18
Registriert: Donnerstag 17. Januar 2019, 21:17

Hallo zusammen,

ich bin dabei mich etwas in die Thematik Multiprocessing einzuarbeiten. Dabei bin ich auf etwas gestoßen, was aus meiner Sicht keinen Sinn ergibt.

Hier der Code:

Code: Alles auswählen

import multiprocessing as mp
from multiprocessing import Process
import time

print(mp.cpu_count())

def myfunc(s_string, i_wait):
    time.sleep(i_wait)
    print(f'time from {s_string} exceeded')

if __name__ == '__main__':
    proc_a = Process(target=myfunc('a', 2))
    print('a created')
    proc_b = Process(target=myfunc('b', 4))
    print('b created')
    proc_c = Process(target=myfunc('c', 6))
    print('c created')
    proc_a.start()
    proc_b.start()
    proc_c.start()
    proc_a.join()
    proc_b.join()
    proc_c.join()
Und hier die Ausgabe:

Code: Alles auswählen

8
time from a exceeded
a created
time from b exceeded
b created
time from c exceeded
c created
8
8
8
Nun zu den Punkten, die ich nicht verstehe.
  • Warum wird Linie 5 insgesamt 4 mal ausgeführt?
    Habe da zwar eine Vermutung, welche für mich aber keinen Sinn ergibt:
    Sie wird beim Ausführen einmalig und dann in jeden Process nochmals ausgeführt.
    Aber die Abfrage ist doch nicht Teil der Funktion "myfunc()", die ich später den Prozessen zuweise.
  • Warum werden die Prozesse "proc_x" ausgeführt bevor ich sie über "proc_x.start()" starte?
Ich hoffe ihr könnt mir da helfen.

Grüße
TenchiMuyo1984
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du begehst den üblichen Fehler: statt als Target ein callable zu übergeben, übergibst du den Rückgabewert des Aufrufs. Der None ist. Schau dir nochmal ganz genau die Beispiele an, und wo da Klammern sind, und wo nicht.
Thants
User
Beiträge: 34
Registriert: Dienstag 1. Dezember 2020, 12:00

Zum ersten Punkt, das steht in der Dokumentation unter "Programming Guidelines". Je nach Start-Methode wird das Hauptmodul in den neuen Prozessen nochmal importiert, weshalb Zeile 5 dann mehrfach ausgeführt wird. In deinem eigentlichen Programm solltest du also direkt auf Modul-Ebene keinen Code haben, der sofort ausgeführt wird.
TenchiMuyo1984
User
Beiträge: 18
Registriert: Donnerstag 17. Januar 2019, 21:17

__deets__ hat geschrieben: Sonntag 30. Mai 2021, 09:50 Du begehst den üblichen Fehler: statt als Target ein callable zu übergeben, übergibst du den Rückgabewert des Aufrufs. Der None ist. Schau dir nochmal ganz genau die Beispiele an, und wo da Klammern sind, und wo nicht.
Ahhhh,
habe das schlicht vergessen :idea:

Code: Alles auswählen

, args=(
Thants hat geschrieben: Sonntag 30. Mai 2021, 10:10 Zum ersten Punkt, das steht in der Dokumentation unter "Programming Guidelines". Je nach Start-Methode wird das Hauptmodul in den neuen Prozessen nochmal importiert, weshalb Zeile 5 dann mehrfach ausgeführt wird. In deinem eigentlichen Programm solltest du also direkt auf Modul-Ebene keinen Code haben, der sofort ausgeführt wird.
Also alles in?:

Code: Alles auswählen

if __name__ == '__main__':
Habs nun so gemacht:

Code: Alles auswählen

import multiprocessing as mp
from multiprocessing import Process
import time


def myfunc(s_string, i_wait):
    time.sleep(i_wait)
    print(f'time from {s_string} exceeded')

if __name__ == '__main__':
    print(mp.cpu_count())

    proc_a = Process(target=myfunc, args=('a', 2))
    print('a created')
    proc_b = Process(target=myfunc, args=('b', 4))
    print('b created')
    proc_c = Process(target=myfunc, args=('c', 6))
    print('c created')
    proc_a.start()
    proc_b.start()
    proc_c.start()
    proc_a.join()
    proc_b.join()
    proc_c.join()
Und die Ausgabe sieht wie folgt aus:

Code: Alles auswählen

8
a created
b created
c created
time from a exceeded
time from b exceeded
time from c exceeded
Danke!
tonikae
User
Beiträge: 90
Registriert: Sonntag 23. Februar 2020, 10:27

Nur mal interesshalber nachgefragt
Gibt es da keine "Task-Map" in der man seine Tasks in einem Block zusammenpacken und starten kann?
Bsp:
Nix Weltbewegendes - ich will drei Tasks gleichzeitig starten und wissen welcher Task am schnellsten beendet ist.
Dafür eben eine "Task-Map"

https://drive.google.com/file/d/1NDZVP ... kLQ97/view

Wie macht man das in Python?
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Meinst du einen Pool?

https://docs.python.org/3/library/multi ... .pool.Pool

Das kann parallel rechnen, und da erste Ergebnis kommt zuerst zurück, wenn man zb imap benutzt.
tonikae
User
Beiträge: 90
Registriert: Sonntag 23. Februar 2020, 10:27

"pool" war zwar nicht ganz das(konkurrierend parallele Tasks kenne ich etwas anders) , aber trotzdem hilfreiche Antwort.....vielen Dank
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

@tonikae: Du solltest Code immer als Text in Code-Tags einstellen. Nicht als Link auf einen Screenshot. Die kaputte Einrückung dort hast du bemerkt? Auch wenn Julia mit End-Tags arbeitet, hilft es beim Lesen erheblich, wenn die in der richtigen Ebene stehen.

Ich sehe nicht die Besonderheit in dem Code auf dem Screenshot. Du tust die "Tasks" ja einfach in eine Datenstruktur. Das ist ja nichts Sprachspezifisches sondern sollte sich in jeder Sprache umsetzen lassen, die Datenstrukturen kennt und bei denen die Tasks/Threads/Nebenläufigkeiten etwas sind, das man dort hinein stecken kann.
__deets__
User
Beiträge: 14541
Registriert: Mittwoch 14. Oktober 2015, 14:29

Ich verstehe nicht, wo der große Unterschied ist. Weil man ein Pool Objekt braucht? Denn die Nutzung sieht doch nahezu gleich aus. Ich kenne Julia nur vom gelegentlich drüber lesen, und kann mir gut vorstellen, dass da Entscheidungen zugunsten “einfach mathematische Probleme lösen” getroffen wurden. Also eben einen impliziten worker Pool, statt explizit, weil es angenehmer zu nutzen ist. Wenn dir das wichtig ist, kannst du auch eine Hilfsmodul mit einem globalen Pool und einer daraus gezogenen task_map Funktion schreiben. Oder ist da sonst noch ein Unterschied, den ich nicht sehe?
Antworten