Запустите процесс и убейте его, если он не закончится в течение часа - PullRequest
20 голосов
/ 01 сентября 2009

Мне нужно сделать следующее в Python. Я хочу порождать процесс (модуль подпроцесса?) И:

  • если процесс завершается нормально, продолжать точно с момента его завершения;
  • если, в противном случае, процесс "застревает" и не завершается (скажем) в течение одного часа, чтобы убить его и продолжить (возможно, еще раз попытаться в цикле).

Какой самый элегантный способ сделать это?

Ответы [ 5 ]

27 голосов
/ 01 сентября 2009

Модуль subprocess станет вашим другом. Запустите процесс, чтобы получить объект Popen, затем передайте его такой функции. Обратите внимание, что это только вызывает исключение по таймауту. При желании вы можете перехватить исключение и вызвать метод kill() в процессе Popen. (kill является новым в Python 2.6, кстати)

import time

def wait_timeout(proc, seconds):
    """Wait for a process to finish, or raise exception after timeout"""
    start = time.time()
    end = start + seconds
    interval = min(seconds / 1000.0, .25)

    while True:
        result = proc.poll()
        if result is not None:
            return result
        if time.time() >= end:
            raise RuntimeError("Process timed out")
        time.sleep(interval)
8 голосов
/ 10 мая 2012

Существует как минимум 2 способа сделать это с помощью psutil , если вы знаете PID процесса. Предполагая, что процесс создан так:

import subprocess
subp = subprocess.Popen(['progname'])

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

import psutil, time

TIMEOUT = 60 * 60  # 1 hour

p = psutil.Process(subp.pid)
while 1:
    if (time.time() - p.create_time) > TIMEOUT:
        p.kill()
        raise RuntimeError('timeout')
    time.sleep(5)

... или просто, вы можете сделать это:

import psutil

p = psutil.Process(subp.pid)
try
    p.wait(timeout=60*60)
except psutil.TimeoutExpired:
    p.kill()
    raise

Кроме того, пока вы занимаетесь этим, вас могут заинтересовать следующие дополнительные API:

>>> p.status()
'running'
>>> p.is_running()
True
>>>
3 голосов
/ 26 января 2017

У меня был похожий вопрос и я нашел этот ответ. Просто для полноты я хочу добавить еще один способ завершения процесса зависания через заданный промежуток времени: библиотека сигналов Python https://docs.python.org/2/library/signal.html

Из документации:

import signal, os

def handler(signum, frame):
    print 'Signal handler called with signal', signum
    raise IOError("Couldn't open device!")

# Set the signal handler and a 5-second alarm
signal.signal(signal.SIGALRM, handler)
signal.alarm(5)

# This open() may hang indefinitely
fd = os.open('/dev/ttyS0', os.O_RDWR)

signal.alarm(0)          # Disable the alarm

Поскольку вы все равно хотели создать новый процесс, это может быть не лучшим решением для вашей проблемы.

1 голос
/ 11 октября 2017

Хороший, пассивный способ также заключается в использовании threading.Timer и настройке функции обратного вызова.

from threading import Timer

# execute the command
p = subprocess.Popen(command)

# save the proc object - either if you make this onto class (like the example), or 'p' can be global
self.p == p

# config and init timer
# kill_proc is a callback function which can also be added onto class or simply a global
t = Timer(seconds, self.kill_proc)

# start timer
t.start()

# wait for the test process to return
rcode = p.wait()

t.cancel()

Если процесс заканчивается вовремя, wait () заканчивается и код продолжается здесь, cancel () останавливает таймер. Если между тем таймер истекает и выполняет kill_proc в отдельном потоке, wait () также будет продолжаться здесь, а cancel () ничего не сделает. По значению rcode вы будете знать, рассчитали ли мы время или нет. Простейший kill_proc: (вы, конечно, можете сделать там что-нибудь еще)

def kill_proc(self):
    os.kill(self.p, signal.SIGTERM)
0 голосов
/ 19 февраля 2019

Koodos Питеру Шиннерсу за его замечательное предложение о модуле subprocess. Я использовал exec() раньше и не имел никакого контроля над временем выполнения и особенно прерывал его. Мой самый простой шаблон для такого рода задач следующий: я просто использую параметр времени ожидания функции subprocess.run() для контроля времени выполнения. Конечно, вы можете получить стандартную ошибку и ошибку, если это необходимо:

from subprocess import run, TimeoutExpired, CalledProcessError

for file in fls:
    try:
        run(["python3.7", file], check=True, timeout=7200)  # 2 hours timeout
        print("scraped :)", file)
    except TimeoutExpired:
        message = "Timeout :( !!!"
        print(message, file)
        f.write("{message} {file}\n".format(file=file, message=message))
    except CalledProcessError:
        message = "SOMETHING HAPPENED :( !!!, CHECK"
        print(message, file)
        f.write("{message} {file}\n".format(file=file, message=message))

...