Python: убить или завершить подпроцесс, когда тайм-аут
Я хотел бы как можно быстрее выполнить подпроцесс. Однако иногда процесс занимает слишком много времени, поэтому я хочу его убить.
Я использую 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
но иногда этот код попытается остановить выполнение следующего раунда.
Stop test/home/lu/workspace/152/treefit/test2for time out
/bin/sh:/home/lu/workspace/153/squib_driver: не найдено --- это следующее исполнение; программа ошибочно останавливает его.
Кто-нибудь знает, как это решить? Я хочу, чтобы вовремя не выполнялся 1 секунда time.sleep(n), часто ждал n секунд. Я не хочу, чтобы он мог выполнить менее 1 секунды
Ответы
Ответ 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 секунд.
Ответ 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'])
Комментарии:
- здесь время тайм-аута в секундах; но легко изменить все, что необходимо, изменив значение
time.sleep()
. time_out
должен быть документирован соответствующим образом;
- в соответствии с тем, что необходимо, здесь, возможно, более целесообразно поднять какое-то исключение.
Документация: я немного потрудился с документацией модуля subprocess
, чтобы понять, что subprocess.Popen
не блокирует; процесс выполняется параллельно (возможно, я не использую здесь правильное слово, но я думаю, что это понятно).
Но поскольку то, что я написал, является линейным в его исполнении, мне действительно нужно дождаться завершения команды, чтобы избежать ошибок в команде, чтобы приостановить ночное выполнение script.
Ответ 3
Я предполагаю, что это общая проблема синхронизации в ориентированном на события программировании с потоками и процессами.
Если вы всегда должны иметь только один подпроцесс, убедитесь, что текущий подпроцесс убит, прежде чем запускать следующий. В противном случае обработчик сигнала может получить ссылку на последний прогон подпроцесса и игнорировать старшего.
Предположим, что подпроцесс A запущен. Перед обработкой сигнала тревоги запускается подпроцесс B. Сразу после этого ваш обработчик сигнала тревоги пытается убить подпроцесс. Поскольку текущий PID (или текущий объект подпроцесса) был установлен в B при запуске подпроцесса, B убивается и A продолжает работать.
Я правильно понял?
Чтобы сделать код более понятным, я бы включил часть, которая создает новый подпроцесс сразу после части, которая убивает текущий подпроцесс. Это будет ясно, что в любой момент работает только один подпроцесс. Обработчик сигналов мог выполнять как подпроцесс, убивая, так и запуская, как если бы он был итерационным блоком, который работает в цикле, в этом случае управляется событиями с сигналом тревоги каждые 1 секунду.
Ответ 4
Вот что я использую:
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)
Ответ 5
Немного сложнее, я добавил ответ чтобы решить аналогичную проблему: Захват stdout, кормление stdin и возможность завершить работу через некоторое время бездействия и/или после некоторого общего времени выполнения.