是否有任何参数或选项可为Python的subprocess.Popen方法设置超时?
类似这样的东西:
subprocess.Popen(['..'], ..., timeout=20)
?
我建议您看一下Timer
class模块中的Timer
。我用它实现了threading
的超时。
首先,创建一个回调:
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()
否则,python程序将一直运行,直到计时器完成运行。
编辑:我被告知存在一种竞争条件,即t.cancel()
subprocess
可能在p
和p.poll()
调用之间终止。我相信以下代码可以解决此问题:
p.kill()
尽管您可能希望清除异常处理以专门处理子流程已经正常终止时发生的特定异常。
subprocess.Popen不会阻止,因此您可以执行以下操作:
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
它的缺点是必须始终等待至少20秒才能完成。
import time
p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
p.kill()
print 'timed out'
else:
print p.communicate()
此输出应为:
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)
可以看到,在第一次执行中,该过程正确完成了(返回代码0),而在第二次执行中,该过程终止了(返回代码-15)。
我尚未在Windows中进行测试;但是,除了更新示例命令之外,我认为它应该可以工作,因为我在文档中没有发现任何不支持thread.join或process.terminate的内容。
您可以做
Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15
使用Twisted的异步处理API。
没有内置python子进程自动超时,因此您将必须构建自己的超时。
这在运行python 2.7.3的Ubuntu 12.10上对我有效
将其放入名为test.py的文件中>
from twisted.internet import reactor, protocol, error, defer class DyingProcessProtocol(protocol.ProcessProtocol): def __init__(self, timeout): self.timeout = timeout def connectionMade(self): @defer.inlineCallbacks def killIfAlive(): try: yield self.transport.signalProcess('KILL') except error.ProcessExitedAlready: pass d = reactor.callLater(self.timeout, killIfAlive) reactor.spawnProcess(DyingProcessProtocol(20), ...)
保存并运行:
#!/usr/bin/python import subprocess import threading class RunMyCmd(threading.Thread): def __init__(self, cmd, timeout): threading.Thread.__init__(self) self.cmd = cmd self.timeout = timeout def run(self): self.p = subprocess.Popen(self.cmd) self.p.wait() def run_the_process(self): self.start() self.join(self.timeout) if self.is_alive(): self.p.terminate() #if your process needs a kill -9 to make #it go away, use self.p.kill() here instead. self.join() RunMyCmd(["sleep", "20"], 3).run_the_process()
python test.py
命令需要20秒才能完成。如果它没有在3秒内终止(不会),则该过程终止。
sleep 20
从运行过程到终止之间有三秒钟的时间。
不幸的是,没有这样的解决方案。我设法使用线程计时器来执行此操作,该计时器将与超时后启动的进程一起启动,但是由于僵尸进程或类似进程,我确实遇到了一些过时的文件描述符问题。
不,没有超时。我猜,您正在寻找的是在一段时间后终止子进程。由于您可以发信号通知子流程,因此您也应该可以将其杀死。
从Python 3.3开始,子流程模块中的阻塞帮助器函数也有proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)
参数。
是,https://docs.python.org/3/library/subprocess.html将使用两个附加功能扩展Popen模块,
对于Linux,您可以使用信号。这取决于平台,因此Windows需要另一种解决方案。不过,它可能适用于Mac。