Seite 1 von 1
Return-Wert aus Multiprocessing bekommen
Verfasst: Montag 30. April 2018, 10:24
von lordzwieback
Hallo,
ich versuche mich gerade an einem Script, in welchem ich Multiprocessing anwenden möchte. Habe mich ein wenig eingelesen und ein kleines Beispiel gebaut, das auch soweit funktioniert. Im Prinzip geht es darum, eine Funktion auszuführen und danach fünf Sekunden zu warten. Ist die Funktion bis zum Ablaufen der Wartezeit nicht fertig, wird sie terminiert.
Momentaner Stand ist folgend:
Funktion:
Code: Alles auswählen
def myFunction(pc, user):
temp = subprocess.Popen([r"C:\Windows\SysNative\query.exe",
"user", user,
"/server",
pc],
stdout=subprocess.PIPE).communicate()[0]
Main:
Code: Alles auswählen
def main():
local_network_user = {
"PC1": "A*******",
"PC2": "R*****",
"PC3": "D***",
"PC4": "S*********"
}
for key in local_network_user:
# start function 'myFunction' as process
process = Process(target=myFunction, args=(key, local_network_user.get(key)))
process.start()
print("Process started...")
# wait for 5 seconds or until process finishes
process.join(5)
print("Waiting...")
# if thread is still active
if process.is_alive():
print("running... let's kill the process...")
process.terminate()
process.join()
So weit so gut, der Prozess wird gestartet, terminiert bei > 5 Sekunden und ansonsten führt er den Befehl aus. Nun brauche ich im realen Script aber den Rückgabewert (Variable 'temp' aus Funktion 'myFuction') und verstehe einfach nicht, wie das funktionieren soll. Habe mir schon einige Beispiele mit multiprocessing.Manager, Value, shared variables usw. angeschaut, aber ich bin wohl zu doof dafür.

Kann mir hier irgendwer verständlich erklären, was ich tun muss, um den Rückgabewert aus der Funktion herauszubekommen? Zurückkommen sollte da ein String.
Danke schonmal fürs Lesen und Grüße,
lordzwieback
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Montag 30. April 2018, 11:06
von __deets__
Zuerstmal bringt dir dein Vorgehen doch genau gar nix. Mühselig mit multiprocessing einen anderen Prozess hochzuziehen, auf den du dann aber direkt wartest - da kannst du deine Query doch gleich direkt starten :K
Und konzeptionell kannst du keine Rückgabe bekommen, wenn du den Prozess killst. Das ist dann undefiniert, es weiß doch keiner, wie weit der Prozess gekommen ist.
Wenn du den NICHT umbringst, dann musst du den Wert in temp natürlich auch per return zurückgeben. Allerdings funktioniert das dann trotzdem nicht mit Process, das ist zu wenig. Du musst da eigentlich noch eine queue oder andere geteilte Datenstruktur mit anlegen. Einfacher ist gleich der Umgang mit einem Pool, und zb dessen map Methode. KIllen darfst du dann aber nicht, sondern musst (was ich denke das ist, das du eigentlich willst) den query subprocess töten.
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Montag 30. April 2018, 12:48
von lordzwieback
Zuerstmal danke für die Hinweise und Tipps.
Dass ich keine Rückgabe erhalte, wenn ich den Prozess kille ist mir jetzt auch aufgefallen. Evtl wird der Prozess ja schon abgebrochen, bevor die Zuweisung von temp in das dict geschieht.
Nochmal zum Wunschablauf:
Der query subprocess wird angeschmissen. Diesen möchte ich, sollte er nach 5 Sekunden keine Rückgabe liefern abbrechen. Grund hierfür: Wird ein Rechner, der angeschaltet ist angesprochen, erhalte ich eine Rückgabe in unter 1 Sekunde. Ist der Rechner ausgeschaltet, dauert der Durchlauf des Befehls (pro Rechner!) bis zu 25 Sekunden. Somit könnte ich pro Komplettdurchlauf des Skripts eine "halbe Ewigkeit" an Zeit sparen.
Deinen Vorschlag mit dem Pool habe ich auch schon entdeckt, nur bin ich da noch nicht ganz dahintergestiegen. Ich werde mir das aber nochmal genauer anschauen.
Und im Prinzip hast du recht, es geht nur darum, den subprocess nach der Wartezeit abzubrechen.
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Montag 30. April 2018, 14:01
von __deets__
Wenn es nur darum geht, den abzubrechen, brauchst du zwar subprocess, aber nicht multiprocessing. Und dort einfach run, das kann schon timeout:
https://docs.python.org/3/library/subprocess.html
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Montag 30. April 2018, 15:04
von lordzwieback
Das mit dem 'run' Befehl war schonmal hilfreich. Es läuft jetzt fast so, wie ich es benötige.
Code: Alles auswählen
def main():
local_network_user = {
"PC1": "A*******",
"PC2": "R*****",
"PC3": "D***",
"PC4": "S*********"
}
for key in local_network_user:
print(key + "/" + local_network_user.get(key))
try:
output = subprocess.run([r"C:\Windows\SysNative\query.exe", "user", local_network_user.get(key), "/server", key], timeout=5)
print("ERGEBNIS: " + str(output))
except subprocess.TimeoutExpired:
print("TIMEOUT USER " + local_network_user.get(key))
continue
Wenn ein Timeout auftaucht, springt er in die Exception und schreibt die Zeile TIMEOUT...
Wenn alles glatt läuft, kommt zwar der ERGEBNIS-print, aber nicht mehr wie vorher mit dem Inhalt, den der Befehl zurückgibt, sondern folgendes:
ERGEBNIS: CompletedProcess(args=['C:\\Windows\\SysNative\\query.exe', 'user', 'A******', '/server', 'PC1'], returncode=1)
Habe dann nochmal unter dem Doc-Link, den du geschickt hattest nachgeschaut. Bin dann auf class subprocess.CompletedProcess (The return value from run(), representing a process that has finished.) gestoßen.
Darunter dann folgendes gefunden:
stdout
Captured stdout from the child process. A bytes sequence, or a string if run() was called with an encoding or errors. None if stdout was not captured.
If you ran the process with stderr=subprocess.STDOUT, stdout and stderr will be combined in this attribute, and stderr will be None.
Nur kann ich weder stdout= weder stderr= als Parameter beim run() Befehl nehmen und wenn ich print("ERGEBNIS: " + output.stdout) teste, erhalte ich immer "None" als Ausgabe. Muss ich dann doch wieder eine andere Herangehensweise finden?
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Montag 30. April 2018, 15:16
von __deets__
Doch, die kannst du beide angeben. Steht klar in der dokumentierten Signatur der Funktion.
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Mittwoch 2. Mai 2018, 07:48
von lordzwieback
Moinmoin,
ich habs gefunden, hattest recht.
Habe es jetzt folgendermaßen laut Doc umgeschrieben:
Zu subprocess.run
This does not capture stdout or stderr by default. To do so, pass PIPE for the stdout and/or stderr arguments.
Code: Alles auswählen
try:
start = datetime.now()
output = subprocess.run([r"C:\Windows\SysNative\query.exe", "user", local_network_user.get(key), "/server", key], stdout=subprocess.PIPE, timeout=5)
print("ERGEBNIS: " + str(output.stdout))
print("KEIN TIMEOUT ZEIT:" + str(datetime.now() - start))
except subprocess.TimeoutExpired:
print("TIMEOUT USER " + local_network_user.get(key))
print("TIMEOUT ZEIT: " + str(datetime.now() - start))
continue
Jetzt erhalte ich die Ausgabe des query Befehls und kann diesen korrekt ausgeben. Aber leider ergibt die Zeitmessung, die ich hier jetzt noch zusätzlich drinhabe, dass der Timeout nicht nach 5 Sekunden, sondern erst nach 20 - 25 Sekunden kommt.
Hier einige print-Ausgaben aus der Console:
TIMEOUT ZEIT: 0:00:21.050983
KEIN TIMEOUT ZEIT: 0:00:00.097088
TIMEOUT ZEIT: 0:00:21.068138
TIMEOUT ZEIT: 0:00:21.070725
KEIN TIMEOUT ZEIT: 0:00:00.086738
Was mache ich denn hier falsch?!
EDIT:
Wenn ich den Parameter stdout=subprocess.PIPE wieder herausnehme, kommt folgendes dabei raus:
KEIN TIMEOUT ZEIT: 0:00:00.112095
KEIN TIMEOUT ZEIT: 0:00:00.091930
TIMEOUT ZEIT: 0:00:05.010635
TIMEOUT ZEIT: 0:00:05.014957
TIMEOUT ZEIT: 0:00:05.014663
Bewirkt der stdout-Parameter, dass der Befehl zwingend durchgeführt werden muss (und ignoriert somit den Timeout-Parameter)?
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Mittwoch 2. Mai 2018, 09:55
von lordzwieback
Ich habe wohl die Lösung für mein Problem gefunden.
Code: Alles auswählen
cmd = [r"C:\Windows\SysNative\query.exe", "user", local_network_user.get(key), "/server", key]
try:
start = datetime.now()
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdout, stderr = proc.communicate(timeout=5)
print("OUTPUT:" + str(stdout))
except subprocess.TimeoutExpired:
print("TERMINATED AFTER: " + str(datetime.now() - start))
Der Timeout als Parameter von
communicate() funktioniert. Wenn der Timeout nicht auftritt, wird korrekt der Output ausgegeben bzw in die Variable
stdout geschrieben und kann dann von dort aus weiter bearbeitet werden.
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Mittwoch 2. Mai 2018, 11:09
von __deets__
Schoen das es klappt. Das der run-Aufruf sich so komisch verhaelt ist ungewoehnlich, das haette ich nicht erwartet. Bei Gelegenheit schaue ich mir das mal an. Ggf. kannst du natuerlich auch einen Bug-Report einstellen im Python bug-tracker!
Re: Return-Wert aus Multiprocessing bekommen
Verfasst: Mittwoch 2. Mai 2018, 11:22
von lordzwieback
Ich nehme an, dass der query Befehl weitere Child-Prozesse startet und dadurch nicht einfach beendet werden kann - ist aber nur eine Vermutung. Die beruht darauf, dass ich (als ich noch den run() Befehl drin hatte) den query durch einen einfachen ping ausgetauscht hatte. Das hat wunderbar funktioniert.
Was weiterhin noch komisch war:
Der Popen() enthält bei meinem PyCharm (aktuellste Version mit Python 3.5) beim Parametervorschlag den timeout. In der offiziellen Doku zum Popen steht der timeout Parameter nicht mehr in den verfügbaren Parametern. Wenn ich den dann ausgeführt habe, hat er mir auch angezeigt etwa... "unknown parameter 'timeout' ".
Bin danach dann durch Zufall auf den .communicate(timeout=5) gestoßen, das hat dann funktioniert.