超时如何终止子进程?

22 投票
6 回答
33895 浏览
提问于 2025-04-16 06:56

我想要尽可能快地重复执行一个子进程。不过,有时候这个进程会花太长时间,所以我想要把它杀掉。
我使用了像下面这样的信号处理:

ppid=pipeexe.pid
signal.signal(signal.SIGALRM, stop_handler)

signal.alarm(1)
.....
def stop_handler(signal, frame):
    print 'Stop test'+testdir+'for time out'
    if(pipeexe.poll()==None and hasattr(signal, "SIGKILL")):
         os.kill(ppid, signal.SIGKILL)
         return False

但是有时候这段代码会试图阻止下一轮的执行。
停止测试/home/lu/workspace/152/treefit/test2因为超时。
/bin/sh: /home/lu/workspace/153/squib_driver: 找不到 ---这是下一次执行;程序错误地停止了它。

有没有人知道怎么解决这个问题?我想要及时停止,而不是每次执行时都等1秒,time.sleep(n)通常会等待n秒。我不想这样,我希望它能在1秒内执行完。

6 个回答

0

我觉得这是一个在事件驱动编程中,涉及线程和进程的常见同步问题。

如果你希望始终只有一个子进程在运行,确保在启动下一个子进程之前,先杀掉当前的子进程。否则,信号处理程序可能会引用上一个运行的子进程,而忽略掉更早的那个。

假设子进程A正在运行。在处理警报信号之前,子进程B被启动。就在这时,你的警报信号处理程序试图杀掉一个子进程。由于当前的进程ID(或者当前的子进程管道对象)在启动子进程时被设置为B的,所以B会被杀掉,而A仍然在运行。

我的猜测对吗?

为了让你的代码更容易理解,我建议在杀掉当前子进程的代码后,紧接着写上创建新子进程的部分。这样可以清楚地表明任何时候只会有一个子进程在运行。信号处理程序可以同时处理杀掉子进程和启动新子进程,就像一个循环中的迭代块一样,在这个情况下是每秒一次的事件驱动。

2

这是我写的一个监控程序,用来观察子进程的执行。我现在经常用它,但我经验不多,可能里面有一些问题:

import subprocess
import time

def subprocess_execute(command, time_out=60):
    """executing the command with a watchdog"""

    # launching the command
    c = subprocess.Popen(command)

    # now waiting for the command to complete
    t = 0
    while t < time_out and c.poll() is None:
        time.sleep(1)  # (comment 1)
        t += 1

    # there are two possibilities for the while to have stopped:
    if c.poll() is None:
        # in the case the process did not complete, we kill it
        c.terminate()
        # and fill the return code with some error value
        returncode = -1  # (comment 2)

    else:                 
        # in the case the process completed normally
        returncode = c.poll()

    return returncode   

使用方法:

 return = subprocess_execute(['java', '-jar', 'some.jar'])

说明:

  1. 这里的监控超时时间是以秒为单位的;不过你可以通过修改 time.sleep() 的值来轻松改变这个时间。time_out 也需要相应地记录下来;
  2. 根据需要,这里可能更合适抛出一些异常。

文档:我在理解 subprocess 模块的文档时遇到了一些困难,才明白 subprocess.Popen 是非阻塞的;也就是说,进程是并行执行的(可能我用的词不太准确,但我想这样说是可以理解的)。

但是因为我写的代码是线性执行的,所以我确实需要等命令完成,并设置一个超时,以避免命令出错导致脚本在夜间执行时暂停。

47

你可以这样做:

import subprocess as sub
import threading

class RunCmd(threading.Thread):
    def __init__(self, cmd, timeout):
        threading.Thread.__init__(self)
        self.cmd = cmd
        self.timeout = timeout

    def run(self):
        self.p = sub.Popen(self.cmd)
        self.p.wait()

    def Run(self):
        self.start()
        self.join(self.timeout)

        if self.is_alive():
            self.p.terminate()      #use self.p.kill() if process needs a kill -9
            self.join()

RunCmd(["./someProg", "arg1"], 60).Run()

这个想法是,你创建一个线程来执行命令,如果超出了设定的时间限制,比如说60秒,就把这个线程杀掉。

撰写回答