Python 超时时终止或终止子进程?

Python 超时时终止或终止子进程?,python,timeout,subprocess,kill,signals,Python,Timeout,Subprocess,Kill,Signals,我希望尽可能快地重复执行子流程。然而,有时这个过程会花太长时间,所以我想杀死它。 我使用信号。信号(…)如下所示: 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 an

我希望尽可能快地重复执行子流程。然而,有时这个过程会花太长时间,所以我想杀死它。 我使用信号。信号(…)如下所示:

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_驱动程序:未找到---这是下一次执行;程序错误地停止了它


有人知道如何解决这个问题吗?我想及时停止,而不是执行1秒的时间。睡眠(n)通常等待n秒。我不希望它能在1秒之内执行,我想这是线程和进程面向事件编程中常见的同步问题

如果始终只运行一个子流程,请确保在运行下一个子流程之前先终止当前子流程。否则,信号处理程序可能会获取对上一个子流程运行的引用,并忽略较旧的子流程

假设子进程A正在运行。在处理报警信号之前,启动子流程B。在此之后,报警信号处理程序将尝试终止子进程。当启动子流程时,当前PID(或当前子流程管道对象)设置为B时,B被终止,A继续运行

我的猜测正确吗


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

您可以执行以下操作:

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秒)时终止该命令。

以下是我作为子流程执行的看门狗编写的内容。我现在经常使用它,但我没有那么多经验,所以可能有一些缺陷:

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   
评论:

  • 这里,看门狗超时以秒为单位;但是,通过更改
    time.sleep()
    值,可以很容易地更改为所需的任何内容。必须相应地记录
    超时
  • 根据需要,这里可能更适合提出一些例外
  • 文档:我对
    子流程
    模块的文档做了一些努力,以理解
    子流程.Popen
    没有阻塞;这个过程是并行执行的(也许我在这里没有使用正确的词,但我认为这是可以理解的)

    但是,由于我编写的脚本在执行过程中是线性的,因此我必须等待命令完成,并留出一段时间,以避免命令中出现错误,从而暂停脚本的夜间执行。

    以下是我使用的方法:

    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)
    

    更为复杂的是,我添加了一个:捕获stdout,输入stdin,并能够在一段时间的不活动和/或某个总体运行时间后终止。

    因此,如果子进程运行超过1秒,基本上你想杀死它并启动下一个吗?这是否正确?如何创建子流程?因为它看起来像表达式ppid=pipeexe.pid正在获取下一个将要运行的子进程!!!因此,如果子进程运行超过1秒,您想终止它并启动下一个进程吗?这是正确的吗?是的,没错。您如何创建子流程?pipeexe=subprocess.Popen(…),因为它看起来像表达式ppid=pipeexe.pid正在获取下一个将要运行的子进程!!我想你可能是对的。但是如何确保下次运行+1时pid不会改变。这个方法就是我在构建系统中用来测试可执行文件的方法。这似乎是迄今为止我见过的最干净的方法之一。只需稍加修改,就可以在linux上使用os.kill(self.p.pid,signal.SIGKILL)(或SIGTERM后跟SIGKILL)在Python<2.6上工作。还可以将self.p.wait()替换为self.out,self.err=self.p.communicate(),以避免在子进程填满stdout/stderr管道时阻塞子进程。我粘贴了此代码,并且我的命令似乎启动正常-但是,我需要一次运行一个线程。启动一个进程,让它自然完成,或者如果需要的时间太长,则终止它,然后重新启动。是否可以从中获取Popen的返回值?@galois:
    Run()
    方法执行
    self.Start()
    语句后,
    RunCmd
    实例的
    self.p
    包含已创建的
    子流程.Popen
    实例,因此您可以通过这种方式访问它。你为什么要这个?
    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)