为什么我必须使用.wait()和python的子进程模块?

时间:2010-11-12 13:35:29

标签: python subprocess

我正在通过Linux上的Python子进程模块运行Perl脚本。使用变量输入多次调用运行脚本的函数。

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()
调用脚本后,我真的不会卡住。但是,我想找出为什么我不能使用子进程多次运行脚本,并让脚本并行进行这些计算,而不必等待它在每次运行之间完成。

更新

罪魁祸首并不那么令人兴奋:perl脚本使用了为每次执行重写的公共文件。

然而,我从中学到的教训是垃圾收集器在开始运行后不会删除该进程,因为一旦我将其整理出来,这对我的脚本没有任何影响。

4 个答案:

答案 0 :(得分: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脚本的多次调用?

答案 1 :(得分:1)

你必须调用wait()才能要求“等待”popen的结尾。

由于popen在后台执行perl脚本,如果你不等待(),它将在对象“进程”的生命周期结束时停止...这是在script_runner的末尾。

答案 2 :(得分: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对象被销毁

答案 3 :(得分: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()

因此您的流程将并行运行