Python子进程超时?

2024-04-26 06:11:51 发布

您现在位置:Python中文网/ 问答频道 /正文

是否有任何参数或选项来设置Python的subprocess.Popen方法的超时?

像这样的:

subprocess.Popen(['..'], ..., timeout=20)


Tags: 方法参数选项timeoutsubprocesspopen
3条回答

subprocess.Popen不阻塞,因此您可以执行以下操作:

import time

p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
  p.kill()
  print 'timed out'
else:
  print p.communicate()

它有一个缺点,你必须等待至少20秒才能完成。

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

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

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)

其输出应为:

Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15

可以看到,在第一次执行中,进程正确完成(返回代码0),而在第二次执行中,进程被终止(返回代码-15)。

我没有在windows中进行过测试;但是,除了更新示例命令之外,我认为它应该可以工作,因为我在文档中没有发现任何说明thread.join或process.terminate不受支持的内容。

我建议查看线程模块中的Timer class。我用它来实现Popen的超时。

首先,创建回调:

    def timeout( p ):
        if p.poll() is None:
            print 'Error: process taking too long to complete--terminating'
            p.kill()

然后打开进程:

    proc = Popen( ... )

然后创建一个计时器,调用将进程传递给它的回调。

    t = threading.Timer( 10.0, timeout, [proc] )
    t.start()
    t.join()

在程序的稍后部分,您可能需要添加行:

    t.cancel()

否则,python程序将一直运行,直到计时器完成运行为止。

编辑:有人告诉我,在p.poll()和p.kill()调用之间存在子进程p可能终止的竞争条件。我相信下面的代码可以解决这个问题:

    import errno

    def timeout( p ):
        if p.poll() is None:
            try:
                p.kill()
                print 'Error: process taking too long to complete--terminating'
            except OSError as e:
                if e.errno != errno.ESRCH:
                    raise

尽管您可能希望清除异常处理,以便仅处理子流程已正常终止时发生的特定异常。

相关问题 更多 >