2010-11-12 1 views
6

Я запускаю скрипт Perl через модуль подпроцесса в Python на Linux. Функция, которая запускает скрипт, вызывается несколько раз с переменным входом.Почему я должен использовать .wait() с модулем подпроцесса python?

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) 

Однако, если я запустил эту функцию, скажем, дважды, выполнение первого процесса прекратится, когда начнется второй процесс. Я могу получить желаемое поведение, добавив

process.wait() 

после вызова сценария, поэтому я не застрял. Тем не менее, я хочу узнать, почему я не могу запускать скрипт с помощью подпроцесса столько раз, сколько хочу, и чтобы скрипт выполнял эти вычисления параллельно, не дожидаясь завершения его между каждым прогоном.

UPDATE

Преступник не был настолько захватывающим: сценарий Perl используется общий файл, который был переписан для каждого исполнения.

Однако урок, который я узнал из этого, состоял в том, что сборщик мусора не удаляет этот процесс после его запуска, поскольку это не повлияло на мой сценарий, как только я его разобрал.

+1

вы уверены, что второй завершает? Вы имеете в виду один и тот же * вызов дважды (тот же 'variable_input')? – knitti

+0

Второй заканчивается, как и ожидалось. Я имею в виду разные переменные. Например, «test_1» и «test_2». – Viktiglemma

+0

Что касается попытки сохранить ссылку на оба процесса, например. возвращает процесс и сохраняет его в переменной? – knitti

ответ

2

Если вы используете Unix, и хотите запускать многие процессы в фоновом режиме, вы можете использовать subprocess.Popen таким образом:

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

Выходной сигнал выглядит примерно так:

% 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 

Я не уверен, почему вы получаете странное поведение, когда не звоните .wait(). Однако приведенный выше сценарий предлагает (по крайней мере, в unix), что сохранение subprocess.Popen(...) процессов в списке или наборе не требуется. Какая бы проблема ни была, я не думаю, что это связано с сбором мусора.

PS. Возможно, ваши сценарии perl конфликтуют каким-то образом, что приводит к тому, что один из них заканчивается ошибкой при запуске другого. Вы пытались запустить несколько вызовов в Perl-скрипт из командной строки?

+0

Проблема заключалась в том, что в сценариях использовался общий файл, который был перезаписан. Теперь я запускаю программы параллельно и не добавляя их в список, чтобы защитить объект от сборщика мусора. – Viktiglemma

+0

@Viktiglemma: Ах. Рад, что вы решили проблему. – unutbu

1

Вы должны позвонить wait(), чтобы попросить «подождать» окончания вашего popen.

Как popen исполняет в фоновом режиме скрипт perl, если вы не ждете(), он будет остановлен в конце жизни объекта «process», который находится в конце script_runner.

+0

Другими словами ... Если процесс выходит из сферы действия, он станет пригодным для сбора мусора. Невозможно узнать, когда объект будет * собран, но вы не должны полагаться на это. Ожидание вызова явно. – extraneon

+1

@extraneon: Python подсчитывается по ссылке, поэтому, как только никто не держит ссылку на 'process', объект будет удален, а подпроцесс остановлен. –

+0

Несмотря на это, кажется, что даже если я храню объект в списке, процессы все еще прерываются. – Viktiglemma

1

По словам ericdupo, задача будет убит, потому что вы перезаписать process переменную с новым Popen объекта, а так как нет больше никаких ссылок на предыдущий Popen объект, он будет уничтожен сборщиком мусора.Вы можете предотвратить это, сохраняя ссылку на свои объекты где-то, как список:

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) 

Это должно быть достаточно, чтобы предотвратить предыдущий Popen объект от разрушения

+0

Я пробовал этот код точно, но результат остался прежним. Я думаю, что это больше, чем сборщик мусора. – Viktiglemma

+0

странно, я протестировал его с помощью «трудоемкой» команды, и она отлично работала, проблема должна быть где-то в другом месте. Мне жаль, что это не решает вашу проблему. – MatToufoutu

+0

Извините: вы правы! Проблема заключалась в том, что все сценарии использовали общий файл, который был перезаписан каждый раз. Однако, как только я получил его на работу, мне не было необходимости добавлять процессы в список, чтобы избежать сбора мусора. – Viktiglemma

0

Я думаю, что вы хотите сделать

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

так что ваш процесс будет выполняться параллельно

Смежные вопросы