28

我需要在 Python 中执行以下操作。我想生成一个进程(子进程模块?),并且:

  • 如果进程正常结束,则从它终止的那一刻开始继续;
  • 如果,否则,进程“卡住”并且没有在(比如说)一小时内终止,则杀死它并继续(可能再试一次,在一个循环中)。

完成此任务的最优雅方法是什么?

4

5 回答 5

33

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)
于 2009-08-31T21:17:10.553 回答
10

只要您知道进程 PID ,至少有两种方法可以使用psutil执行此操作。假设流程是这样创建的:

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
>>>
于 2012-05-10T18:19:37.207 回答
4

我有一个类似的问题并找到了这个答案。只是为了完整起见,我想添加一种在给定时间后如何终止挂起进程的方法: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

由于您无论如何都想生成一个新进程,但这可能不是解决您的问题的最佳解决方案。

于 2017-01-26T03:57:45.830 回答
1

一个不错的被动方式也是使用 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)
于 2017-10-11T09:20:44.327 回答
0

Koodos 感谢 Peter Shinners 关于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))

于 2019-02-18T22:59:50.030 回答