如何限制使用子进程时程序的执行时间?

7 投票
3 回答
6696 浏览
提问于 2025-04-16 06:10

我想用子进程来运行一个程序,并且我需要限制它的执行时间。比如说,如果它运行超过2秒,我想把它结束掉。

对于一般的程序,使用kill()函数可以很好地结束它。但是如果我尝试运行/usr/bin/time something,kill()就无法真正结束这个程序。

我下面的代码似乎不太管用,程序还是在继续运行。

import subprocess
import time

exec_proc = subprocess.Popen("/usr/bin/time -f \"%e\\n%M\" ./son > /dev/null", stdout = subprocess.PIPE, stderr = subprocess.STDOUT, shell = True)

max_time = 1
cur_time = 0.0
return_code = 0
while cur_time <= max_time:
    if exec_proc.poll() != None:
        return_code = exec_proc.poll()
        break
    time.sleep(0.1)
    cur_time += 0.1

if cur_time > max_time:
    exec_proc.kill()

3 个回答

0

我使用了os.kill()这个函数,但不确定它是否在所有操作系统上都能正常工作。
下面是伪代码,详细信息可以查看Doug Hellman的页面。

proc = subprocess.Popen(['google-chrome'])                                               
os.kill(proc.pid, signal.SIGUSR1)</code>
9

如果你使用的是Python 2.6或更高版本,可以使用multiprocessing模块。

from multiprocessing import Process

def f():
    # Stuff to run your process here

p = Process(target=f)
p.start()
p.join(timeout)
if p.is_alive():
    p.terminate()

其实,multiprocessing这个模块并不适合这个任务,因为它只是用来控制一个线程运行的时间。你无法控制这个线程可能会启动的任何子线程。正如singularity所说,使用signal.alarm是更常见的做法。

import signal
import subprocess

def handle_alarm(signum, frame):
    # If the alarm is triggered, we're still in the exec_proc.communicate()
    # call, so use exec_proc.kill() to end the process.
    frame.f_locals['self'].kill()

max_time = ...
stdout = stderr = None
signal.signal(signal.SIGALRM, handle_alarm)
exec_proc = subprocess.Popen(['time', 'ping', '-c', '5', 'google.com'],
                             stdin=None, stdout=subprocess.PIPE,
                             stderr=subprocess.STDOUT)
signal.alarm(max_time)
try:
    (stdout, stderr) = exec_proc.communicate()
except IOError:
    # process was killed due to exceeding the alarm
finally:
    signal.alarm(0)
# do stuff with stdout/stderr if they're not None
3

在你的命令行里这样做:

perl -e 'alarm shift @ARGV; exec @ARGV' <timeout> <your_command>

这条命令会运行<your_command>,并在<timeout>秒后终止它。

举个简单的例子:

# set time out to 5, so that the command will be killed after 5 second 
command = ['perl', '-e', "'alarm shift @ARGV; exec @ARGV'", "5"]

command += ["ping", "www.google.com"]

exec_proc = subprocess.Popen(command)

如果你想用Python来实现这个功能,可以使用signal.alarm(),但原理是一样的。

撰写回答