在循环中运行外部程序并设置最大时间限制
我想要一个Python脚本,它可以循环运行一个外部程序,并且每次运行这个程序的时间不能超过设定的最大值。如果超过了这个时间,就要强制结束这个程序。请问有什么好的方法可以做到这一点呢?
谢谢!
2 个回答
如果你能使用 Python 3.3
来自 文档,
subprocess.call(args, *, stdin=None, stdout=None, stderr=None, shell=False, timeout=None)
subprocess.call(["ls", "-l"]) 0
subprocess.call("exit 1", shell=True) 1
这样就可以了。
如果你想在Python中运行一个外部程序,通常会使用subprocess模块。
当然,你也可以自己实现子进程的处理,比如使用os.fork()
和os.execve()
(或者它的其他exec*
相关函数)... 你可以随意处理文件描述符和信号。不过,subprocess.Popen()
这个函数已经实现了大部分你需要的功能。
如果你想让程序在一段时间后自动结束,可以让你的Python脚本在超时后杀掉它。当然,你需要先检查一下这个进程是否已经完成。下面是一个非常简单的例子(使用了shlex模块中的split
函数来提高可读性):
from shlex import split as splitsh
import subprocess
import time
TIMEOUT=10
cmd = splitsh('/usr/bin/sleep 60')
proc = subprocess.Popen(cmd)
time.sleep(TIMEOUT)
pstatus = proc.poll()
if pstatus is None:
proc.kill()
# Could use os.kill() to send a specific signal
# such as HUP or TERM, check status again and
# then resort to proc.kill() or os.kill() for
# SIGKILL only if necessary
正如所提到的,有几种方法可以结束你的子进程。注意我检查的是"is None
"而不是直接测试pstatus
的真假。如果你的进程以零的退出值完成(通常表示没有错误发生),那么简单地测试proc.poll()
的结果可能会把完成状态和仍在运行的进程状态搞混。
还有几种方法可以判断是否已经过了足够的时间。在这个例子中,我们使用了睡眠,这样做有点傻,因为我们本可以做其他事情。这就让我们的Python进程(外部程序的父进程)闲着没事做。
你可以用time.time()
来记录开始时间,然后启动你的子进程,接着做其他工作(比如启动其他子进程),并在达到你想要的超时时间之前检查时间(也许在其他活动的循环中)。
如果你的其他活动涉及文件或套接字(网络)操作,那么你可能需要考虑使用select模块,它可以返回一个可读、可写或准备好处理“异常”事件的文件描述符列表。select.select()
函数也可以接受一个可选的“超时”值。调用select.select([],[],[],x)
基本上和time.sleep(x)
是一样的(如果我们没有提供任何文件描述符供它选择的话)。
如果不使用select.select()
,你也可以使用fcntl模块将你的文件描述符设置为非阻塞模式,然后使用os.read()
(不是普通文件对象的.read()
方法,而是来自os模块的底层功能)。再次强调,尽量使用更高级的接口,只有在必须时才使用底层函数。(如果你使用非阻塞I/O,那么所有的os.read()
或类似操作都必须在异常处理块中进行,因为Python会将“-EWOULDBLOCK”条件表示为OSError(异常),例如:“OSError: [Errno 11] Resource temporarily unavailable”(在Linux上)。错误的具体编号可能因操作系统而异。不过,使用来自errno模块的-EWOULDBLOCK
值在POSIX系统上应该是可移植的。
(我知道我在这里有点偏题,但关于你的程序如何在子进程运行外部程序时做一些有用的事情,实际上是如何管理它们的超时的自然延伸)。
关于非阻塞文件I/O的复杂细节(包括与MS Windows的可移植性问题)在这里以前讨论过:Stackoverflow: non-blocking read on a stream in Python
正如其他人所评论的,提出更详细的问题并附上简短、集中展示你已经做过的努力的代码片段会更好。通常你不会在这里找到愿意写教程而不是回答的人。