2010-11-12 10 views
6

Używam skryptu Perla przez moduł podprocesu w Pythonie na Linux. Funkcja uruchamiająca skrypt jest wywoływana kilka razy ze zmiennym wejściem.Dlaczego muszę używać .wait() z modułem podprocesowym Pythona?

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) 

Jeśli jednak uruchomić tę funkcję, powiedzmy, dwa, wykonanie pierwszego procesu zatrzyma się, gdy rozpoczyna się drugi proces. Mogę uzyskać pożądane zachowanie, dodając po wywołaniu skryptu:

, więc nie utknąłem. Chcę jednak dowiedzieć się, dlaczego nie mogę uruchamiać skryptu przy użyciu podprocesu tyle razy, ile chcę, i skrypt powinien wykonywać te obliczenia równolegle, bez czekania na zakończenie każdego z nich.

UPDATE

Sprawca nie było tak ekscytujące: skrypt Perl stosowany wspólny plik, który został przepisany dla każdej realizacji.

Jednak lekcja, której się nauczyłam, polegała na tym, że śmieciarz nie usuwał procesu po uruchomieniu, ponieważ nie miało to żadnego wpływu na mój skrypt, kiedy go rozwiązałem.

+1

czy jesteś pewien, że drugi się zakończy? Czy masz na myśli * to samo * wywołanie dwa razy (to samo 'variable_input')? – knitti

+0

Drugi kończy się zgodnie z oczekiwaniami. Mam na myśli różne "variable_input". Na przykład "test_1" i "test_2". – Viktiglemma

+0

co z próbą zachowania odniesienia do obu procesów, np. proces powracania i przechowywanie go w zmiennej? – knitti

Odpowiedz

2

Jeśli używasz UNIX i chcesz uruchomić wiele procesów w tle, można użyć subprocess.Popen w ten sposób:

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)) 

Wyjście wygląda mniej więcej tak:

% 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 

Nie jestem pewien, dlaczego otrzymujesz dziwne zachowanie, gdy nie dzwonisz pod numer .wait(). Jednak powyższy skrypt sugeruje (przynajmniej na systemie Unix), że zapisywanie procesów subprocess.Popen(...) na liście lub zestawie nie jest konieczne. Niezależnie od problemu, nie sądzę, że ma to coś wspólnego z odśmiecaniem.

PS. Możliwe, że twoje skrypty perlowe są w jakiś sposób sprzeczne, co powoduje, że jeden kończy się błędem, gdy działa inny. Czy próbowałeś uruchomić wiele wywołań do skryptu Perla z wiersza poleceń?

+0

Problem polegał na tym, że skrypty używały wspólnego pliku, który został nadpisany. Teraz uruchamiam programy równolegle z dodawaniem ich do listy i bez dodawania ich, aby zabezpieczyć obiekt przed zbieraczem. – Viktiglemma

+0

@Viktiglemma: Ah. Cieszę się, że rozwiązałeś problem. – unutbu

1

Musisz zadzwonić wait(), aby poprosić o "czekanie" na zakończenie twojego popen.

Jako, że popen wykonuje w tle skrypt perla, jeśli nie będziesz czekać(), zostanie zatrzymany na końcu procesu "process", czyli na końcu script_runner.

+0

Innymi słowy ... Jeśli proces wyjdzie poza zakres, zostanie zakwalifikowany do usuwania śmieci. Nie ma sposobu, aby dowiedzieć się, kiedy obiekt faktycznie * zostanie * zebrany, ale nie powinieneś na nim polegać. Zadzwoń wyraźnie do połączenia. – extraneon

+1

@extraneon: Python jest liczony jako odniesienie, więc gdy tylko nikt nie będzie odwoływał się do "process", obiekt zostanie usunięty, a podproces zostanie zatrzymany. –

+0

Bez względu na to, wydaje się, że nawet jeśli przechowuję obiekt na liście, procesy nadal są przerywane. – Viktiglemma

1

Jak powiedział przez ericdupo, zadanie zostanie zabity, ponieważ zastąpi zmienną processPopen z nowym obiektem, a ponieważ nie ma więcej odniesień do poprzedniego Popen obiektu, jest on zniszczony przez garbage collector.Można temu zapobiec przez utrzymywanie odniesienie do swoich obiektów gdzieś, jak listy:

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) 

To powinno wystarczyć, aby zapobiec poprzednią Popen obiekt przed zniszczeniem

+0

Próbowałem dokładnie tego kodu, ale wynik pozostaje ten sam. Myślę, że to coś więcej niż śmieciarz. – Viktiglemma

+0

Dziwne, testowałem go za pomocą "czasochłonnego" polecenia, i działało dobrze, problem musi być gdzieś indziej. Przykro mi, że to nie rozwiązuje twojego problemu. – MatToufoutu

+0

Przykro mi, masz rację! Problem polegał na tym, że wszystkie skrypty używały wspólnego pliku, który został nadpisany za każdym razem. Jednak po uruchomieniu go nie było konieczne dodawanie procesów do listy, aby uniknąć zbędnego zbierania śmieci. – Viktiglemma

0

myślę chcesz zrobić

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() 

, więc Twój proces będzie działać równolegle

Powiązane problemy