2010-11-12 6 views
6

Ich führe ein Perl-Skript durch das Unterprozessmodul in Python unter Linux. Die Funktion, die das Skript ausführt, wird mehrmals mit variabler Eingabe aufgerufen.Warum muss ich .wait() mit dem Subprozessmodul von Python verwenden?

Allerdings, wenn ich diese Funktion zweimal ausführen, wird die Ausführung des ersten Prozesses gestoppt, wenn der zweite Prozess startet. Ich kann mein gewünschtes Verhalten durch Hinzufügen

process.wait() 

nach dem Aufruf des Skripts bekommen, so bin ich nicht wirklich fest. Ich möchte jedoch herausfinden, warum ich das Skript nicht so oft mit Subprozess ausführen kann, wie ich möchte, und das Skript diese Berechnungen parallel ausführen lassen, ohne darauf warten zu müssen, dass es zwischen den einzelnen Ausführungen beendet wird.

UPDATE

Der Täter war nicht so aufregend: der Perl-Skript, eine gemeinsame Datei verwendet, die für jede Ausführung neu geschrieben wurden.

Die Lektion, die ich daraus gelernt habe, war jedoch, dass der Garbage Collector den Prozess nicht löscht, sobald er gestartet wird, weil dies keinen Einfluss auf mein Skript hatte, nachdem ich es aussortiert hatte.

+1

Sind Sie sicher, dass der zweite abgeschlossen ist? Meinst du den * self * Aufruf zweimal (selbe 'variable_input')? – knitti

+0

Die zweite wird wie erwartet abgeschlossen. Ich meine mit verschiedenen 'variable_input'. Zum Beispiel "test_1" und "test_2". – Viktiglemma

+0

Was ist mit dem Versuch, einen Verweis auf beide Prozesse, z. Prozess zurückgeben und in einer Variablen speichern? – knitti

Antwort

2

Wenn Sie Unix verwenden, und wünschen viele Prozesse im Hintergrund laufen zu lassen, könnten Sie subprocess.Popen auf diese Weise verwenden:

x_fork_many.py:

import subprocess 
import os 
import sys 
import time 
import random 
import gc # This is just to test the hypothesis that garbage collection of p=Popen() causing the problem. 

# This spawns many (3) children in quick succession 
# and then reports as each child finishes. 
if __name__=='__main__': 
    N=3 
    if len(sys.argv)>1: 
     x=random.randint(1,10) 
     print('{p} sleeping for {x} sec'.format(p=os.getpid(),x=x)) 
     time.sleep(x) 
    else: 
     for script in xrange(N): 
      args=['test.py','sleep'] 
      p = subprocess.Popen(args) 
     gc.collect() 
     for i in range(N): 
      pid,retval=os.wait() 
      print('{p} finished'.format(p=pid)) 

Die Ausgabe etwa wie folgt aussieht:

% x_fork_many.py 
15562 sleeping for 10 sec 
15563 sleeping for 5 sec 
15564 sleeping for 6 sec 
15563 finished 
15564 finished 
15562 finished 

Ich bin mir nicht sicher, warum Sie das seltsame Verhalten erhalten, wenn Sie nicht anrufen .wait(). Das obige Skript schlägt jedoch (zumindest unter Unix) vor, dass das Speichern von subprocess.Popen(...) Prozessen in einer Liste oder Gruppe nicht erforderlich ist. Was auch immer das Problem ist, ich glaube nicht, dass es mit der Speicherbereinigung zu tun hat.

PS. Vielleicht sind Ihre Perl-Skripte in irgendeiner Weise in Konflikt, was dazu führt, dass ein Fehler bei der Ausführung eines anderen endet. Haben Sie versucht, mehrere Aufrufe des Perl-Skripts von der Befehlszeile aus zu starten?

+0

Das Problem bestand darin, dass die Skripts eine gemeinsame Datei verwendeten, die überschrieben wurde. Jetzt führe ich die Programme parallel mit und ohne sie zu einer Liste hinzuzufügen, um das Objekt vor dem Garbage Collector zu schützen. – Viktiglemma

+0

@Vigtiglemma: Ah. Freut mich, dass du das Problem gelöst hast. – unutbu

1

Sie müssen wait() aufrufen, um zu fragen, ob Sie das Ende Ihres popen "warten" wollen.

Als Popen führen im Hintergrund das Perl-Skript, wenn Sie nicht warten(), wird es am Ende des Lebens des Objekts "Prozess" gestoppt ... das ist am Ende von script_runner.

+0

Mit anderen Worten: Wenn der Prozess den Gültigkeitsbereich verlässt, wird er für die Garbage Collection zugelassen. Es gibt keine Möglichkeit zu wissen, wann das Objekt * tatsächlich * gesammelt wird, aber Sie sollten sich nicht darauf verlassen. Call warte explizit. – extraneon

+1

@extraneon: Python ist eine Referenzzählung, so dass, sobald niemand einen Verweis auf "process" hält, das Objekt gelöscht und der Subprozess gestoppt wird. –

+0

Unabhängig davon, es scheint, dass selbst wenn ich das Objekt in einer Liste speichern, die Prozesse noch unterbrochen sind. – Viktiglemma

1

Wie ericdupo sagt, wird die Aufgabe, weil Sie Ihre process Variable mit einem neuen Popen Objekt überschrieben werden getötet, und da es keine weiteren Hinweise auf Ihr bisheriges Popen Objekt ist, wird es von der Garbage Collector zerstört.Sie können dies verhindern, indem Sie einen Verweis auf Ihre Objekte irgendwo zu halten, wie eine Liste:

processes = [] 
def script_runner(variable_input): 

    out_file = open('out_' + variable_input, 'wt') 
    error_file = open('error_' + variable_input, 'wt') 

    process = subprocess.Popen(['perl', 'script', 'options'], shell=False, 
          stdout=out_file, stderr=error_file) 
    processes.append(process) 

Dies sollte Ihr vorheriges Popen Objekt aus genug sein, um zu verhindern, dass

zerstört
+0

Ich habe diesen Code genau ausprobiert, aber das Ergebnis bleibt gleich. Ich denke, da ist mehr dran als der Müllsammler. – Viktiglemma

+0

seltsam, ich habe es mit einem "zeitraubenden" Befehl getestet, und es hat gut funktioniert, das Problem muss woanders dann sein. Es tut mir leid, dass dies Ihr Problem nicht löst. – MatToufoutu

+0

Es tut mir leid: Sie haben Recht! Das Problem war, dass die Skripte alle eine gemeinsame Datei verwendeten, die jedes Mal überschrieben wurde. Sobald ich es jedoch zum Laufen brachte, war es nicht notwendig, dass ich die Prozesse zu einer Liste hinzufügte, um den Müllsammler zu vermeiden. – Viktiglemma

0

Ich glaube, Sie

tun wollen
list_process = [] 
def script_runner(variable_input): 

    out_file = open('out_' + variable_input, 'wt') 
    error_file = open('error_' + variable_input, 'wt') 

    process = subprocess.Popen(['perl', 'script', 'options'], shell=False, 
          stdout=out_file, stderr=error_file) 
    list_process.append(process) 
#call several times script_runner 
for process in list_process: 
    process.wait() 

damit Ihr Prozess parallel ausgeführt wird

Verwandte Themen