超时时杀死或终止子进程?

问题描述 投票:0回答:6

我想尽可能快地重复执行一个子流程。但是,有时候这个进程会耗时太长,所以我想杀掉它。 我使用 signal.signal(...) 如下所示:

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: not found ---这是下一次执行;程序错误地停止了它。

有人知道如何解决这个问题吗?我想及时停止不执行 1 秒 time.sleep(n) 经常等待 n 秒。我不希望我希望它可以执行不到 1 秒

python timeout subprocess kill signals
6个回答
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 秒)则终止它。


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
没有阻塞;该过程是并行执行的(也许我在这里使用的词不正确,但我认为这是可以理解的)。

但是由于我写的是线性执行的,所以我真的必须等待命令完成,超时以避免命令中的错误暂停脚本的夜间执行。


0
投票

我猜这是在线程和进程的面向事件编程中常见的同步问题。

如果您应该始终只运行一个子进程,请确保在运行下一个子进程之前终止当前子进程。否则信号处理程序可能会引用最后一个子进程运行并忽略旧的。

假设子进程A正在运行。在处理报警信号之前,启动子进程 B。紧接着,您的警报信号处理程序会尝试终止子进程。由于当前 PID(或当前子进程管道对象)在启动子进程时被设置为 B,B 被杀死而 A 继续运行。

我的猜测是否正确?

为了使您的代码更易于理解,我将在终止当前子进程的部分之后包括创建新子进程的部分。这将明确在任何时候都只有一个子进程在运行。信号处理程序可以执行子进程的终止和启动,就好像它是循环运行的迭代块一样,在这种情况下,事件驱动,每 1 秒发出一次警报信号。


0
投票

这是我用的:

class KillerThread(threading.Thread):
  def __init__(self, pid, timeout, event ):
    threading.Thread.__init__(self)
    self.pid = pid
    self.timeout = timeout
    self.event = event
    self.setDaemon(True)
  def run(self):
    self.event.wait(self.timeout)
    if not self.event.isSet() :
      try:
        os.kill( self.pid, signal.SIGKILL )
      except OSError, e:
        #This is raised if the process has already completed
        pass    

def runTimed(dt, dir, args, kwargs ):
  event = threading.Event()
  cwd = os.getcwd()
  os.chdir(dir)
  proc = subprocess.Popen(args, **kwargs )
  os.chdir(cwd)
  killer = KillerThread(proc.pid, dt, event)
  killer.start()

  (stdout, stderr) = proc.communicate()
  event.set()      

  return (stdout,stderr, proc.returncode)

0
投票

有点复杂,我添加了一个 answer 来解决类似的问题:Capturing stdout, feeding stdin, and being able to terminate after an time of inactivity and/or after some overall runtime.


0
投票

subprocess.run
是在 Python 3.5 中添加的,这意味着大多数系统应该已经可以使用此方法访问最新的 Python 版本。这使得设置
timeout
.

变得微不足道

这是一个运行

your-executable
(假设它在工作目录中)的示例,带有
--optional args
参数和60秒的超时:

import subprocess

subprocess.run(['./your-executable', '--optional', 'args'], timeout=60)
如果超时到期,

run
将提高
subprocess.TimeoutExpired
,您也可以处理:

try:
    subprocess.run(['./your-executable', '--optional', 'args'], timeout=60)
except subprocess.TimeoutExpired:
    print('timed out!')
© www.soinside.com 2019 - 2024. All rights reserved.