Убить или завершить подпроцесс по истечении времени ожидания? - PullRequest
16 голосов
/ 11 ноября 2010

Я хотел бы повторно выполнить подпроцесс как можно быстрее. Однако иногда этот процесс занимает слишком много времени, поэтому я хочу его убить. Я использую 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

но иногда этот код будет пытаться остановить выполнение следующего раунда. Стоп тест / домашний / лу / рабочее пространство / 152 / древо / тест2 на время ожидания / bin / sh: / home / lu / workspace / 153 / squib_driver: не найдено --- это следующее выполнение; программа ошибочно останавливает его.

Кто-нибудь знает, как это решить? Я хочу остановиться во времени, а не выполнять 1 секунду time.sleep (n) часто ждут n секунд. Я не хочу, чтобы я мог это выполнить менее чем за 1 секунду

Ответы [ 5 ]

40 голосов
/ 14 мая 2011

Вы можете сделать что-то вроде этого:

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 голосов
/ 01 июня 2011

Вот что я написал как сторожевой таймер для выполнения подпроцесса.Я использую его сейчас очень часто, но у меня нет такого опыта, поэтому, возможно, в нем есть некоторые недостатки:

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 голосов
/ 13 октября 2011

Чуть сложнее, я добавил ответ для решения аналогичной проблемы : захват стандартного вывода, подача стандартного ввода и возможность завершения через некоторое время бездействия и / или после некоторого времени выполнения.*

0 голосов
/ 01 июня 2011

Вот что я использую:

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 голосов
/ 18 ноября 2010

Я полагаю, что это общая проблема синхронизации в событийно-ориентированном программировании с потоками и процессами.

Если у вас всегда должен быть запущен только один подпроцесс, убедитесь, что текущий подпроцесс уничтожен перед запуском следующего. В противном случае обработчик сигнала может получить ссылку на последний запуск подпроцесса и игнорировать более старый.

Предположим, что подпроцесс A работает. Перед обработкой сигнала тревоги запускается подпроцесс B. Сразу после этого ваш обработчик сигнала тревоги пытается уничтожить подпроцесс. Поскольку текущий PID (или текущий объект канала подпроцесса) был установлен на B при запуске подпроцесса, B уничтожается, а A продолжает работать.

Правильно ли мое предположение?

Чтобы облегчить понимание вашего кода, я бы включил часть, которая создает новый подпроцесс сразу после части, которая убивает текущий подпроцесс. Это дало бы понять, что в каждый момент времени работает только один подпроцесс. Обработчик сигнала может выполнять как уничтожение, так и запуск подпроцесса, как если бы это был итерационный блок, который выполняется в цикле, в этом случае управляемый событиями с сигналом тревоги каждую 1 секунду.

...