【问题标题】:Run subprocess in python and get stdout and kill process on timeout在 python 中运行子进程并在超时时获取标准输出和终止进程
【发布时间】:2015-10-07 07:46:02
【问题描述】:

这是我的代码,它启动一个子进程,等待它结束并返回标准输出,或者发生超时并引发异常。常用的是print(Run('python --version').execute())

class Run(object):
    def __init__(self, cmd, timeout=2*60*60):
        self.cmd = cmd.split()
        self.timeout = timeout
        self._stdout = b''
        self.dt = 10
        self.p = None

    def execute(self):
        print("Execute command: {}".format(' '.join(self.cmd)))

        def target():
            self.p = Popen(self.cmd, stdout=PIPE, stderr=STDOUT)
            self._stdout = self.p.communicate()[0]

        thread = Thread(target=target)
        thread.start()

        t = 0
        while t < self.timeout:
            thread.join(self.dt)
            if thread.is_alive():
                t += self.dt
                print("Running for: {} seconds".format(t))
            else:
                ret_code = self.p.poll()
                if ret_code:
                    raise AssertionError("{} failed.\nretcode={}\nstdout:\n{}".format(
                        self.cmd, ret_code, self._stdout))
                return self._stdout

        else:
            print('Timeout {} reached, kill task, pid={}'.format(self.timeout, self.p.pid))
            self.p.terminate()
            thread.join()
            raise AssertionError("Timeout")

问题在于以下情况。我启动的进程会产生更多的子进程。因此,当达到超时时,我用self.p.terminate() 杀死主进程(我使用我的类启动的那个),孩子们还在,我的代码挂在self._stdout = self.p.communicate()[0] 线上。如果我手动终止所有子进程,则会继续执行。

当我杀死整个进程树而不是 self.p.terminate() 时,我尝试了解决方案。

如果主进程自己完成并且它的子进程自己存在,这也不起作用,我没有能力找到并杀死它们。但是他们阻止了self.p.communicate()

有没有办法有效解决这个问题?

【问题讨论】:

  • 它正在将管道句柄泄漏给孙进程。如果您没有源代码控制,我不知道如何防止这种情况发生。但是您可以尝试通过DEBUG_PROCESS 将其作为调试器运行并查找CREATE_PROCESS_DEBUG_EVENT 来存储子进程句柄。然后就可以终止整个进程树了。
  • 如果你打电话给output = subprocess.check_output(command, timeout=timeout)会发生什么?
  • @J.F.Sebastian,我认为它会挂起,因为check_output 假设在终止进程后第二次调用communicate 将成功(第 610-612 行)。由于管道句柄泄露给其他进程,read() 将再次阻塞在读取器线程中,但这次join 没有超时。最好只终止子进程。

标签: python windows python-3.x subprocess stdout


【解决方案1】:

您可以使用 PySys 框架中的 ProcessWrapper - 它以跨平台方式提供了很多此功能作为抽象,即

import sys, os
from pysys.constants import *
from pysys.process.helper import ProcessWrapper
from pysys.exceptions import ProcessTimeout

command=sys.executable
arguments=['--version']
try:
    process = ProcessWrapper(command, arguments=arguments, environs=os.environ, workingDir=os.getcwd(), stdout='stdout.log', stderr='stderr.log', state=FOREGROUND, timeout=5.0)
    process.start()
except ProcessTimeout:
    print "Process timeout"
    process.stop()

如果有兴趣,它位于 SourceForge(http://sourceforge.net/projects/pysys/files/http://pysys.sourceforge.net/)。

【讨论】:

    猜你喜欢
    • 2018-12-06
    • 2015-07-16
    • 2019-03-23
    • 1970-01-01
    • 1970-01-01
    • 2016-12-12
    • 1970-01-01
    • 2021-06-19
    • 1970-01-01
    相关资源
    最近更新 更多