Python子进程超时?
有没有什么参数或者选项可以为Python的subprocess.Popen方法设置一个超时时间?
类似这样:
subprocess.Popen(['..'], ..., timeout=20)
?
10 个回答
5
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不被支持。
7
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秒才能让它完成。
16
我建议你看看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()
在程序的某个地方,你可能想加上这一行:
t.cancel()
否则,Python程序会一直运行,直到定时器完成。
补充说明:有人提醒我,subprocess
的p
可能在p.poll()
和p.kill()
之间结束,这会造成竞争条件。我认为下面的代码可以解决这个问题:
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
不过,你可能想要整理一下异常处理,专门处理当子进程已经正常结束时出现的特定异常。