Python子进程超时?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3733270/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 12:28:33  来源:igfitidea点击:

Python subprocess timeout?

pythontimeoutpipesubprocesspopen

提问by sultan

Is there any argument or options to setup a timeout for Python's subprocess.Popen method?

是否有任何参数或选项可以为 Python 的 subprocess.Popen 方法设置超时?

Something like this:

像这样的东西:

subprocess.Popen(['..'], ..., timeout=20)?

subprocess.Popen(['..'], ..., timeout=20)?

采纳答案by dvntehn00bz

I would advise taking a look at the Timerclassin the threadingmodule. I used it to implement a timeout for a Popen.

我会建议采取看看Timerthreading的模块。我用它来实现一个Popen.

First, create a callback:

首先,创建一个回调:

def timeout( p ):
    if p.poll() is None:
        print 'Error: process taking too long to complete--terminating'
        p.kill()

Then open the process:

然后打开进程:

proc = Popen( ... )

Then create a timer that will call the callback, passing the process to it.

然后创建一个将调用回调的计时器,将进程传递给它。

t = threading.Timer( 10.0, timeout, [proc] )
t.start()
t.join()

Somewhere later in the program, you may want to add the line:

在程序后面的某个地方,您可能需要添加以下行:

t.cancel()

Otherwise, the python program will keep running until the timer has finished running.

否则,python 程序将继续运行,直到计时器完成运行。

EDIT: I was advised that there is a race condition that the subprocesspmay terminate between the p.poll()and p.kill()calls. I believe the following code can fix that:

编辑:我被告知存在subprocessp可能在p.poll()p.kill()调用之间终止的竞争条件。我相信以下代码可以解决这个问题:

import errno

def timeout( p ):
    if p.poll() is None:
        try:
            p.kill()
            print 'Error: process taking too long to complete--terminating'
        except OSError as e:
            if e.errno != errno.ESRCH:
                raise

Though you may want to clean the exception handling to specifically handle just the particular exception that occurs when the subprocess has already terminated normally.

尽管您可能希望清除异常处理以专门处理子进程已正常终止时发生的特定异常。

回答by Abhishek Amit

subprocess.Popen doesn't block so you can do something like this:

subprocess.Popen 不会阻塞,因此您可以执行以下操作:

import time

p = subprocess.Popen(['...'])
time.sleep(20)
if p.poll() is None:
  p.kill()
  print 'timed out'
else:
  print p.communicate()

It has a drawback in that you must always wait at least 20 seconds for it to finish.

它有一个缺点,您必须始终等待至少 20 秒才能完成。

回答by Noufal Ibrahim

Unfortunately, there isn't such a solution. I managed to do this using a threaded timer that would launch along with the process that would kill it after the timeout but I did run into some stale file descriptor issues because of zombie processes or some such.

不幸的是,没有这样的解决方案。我设法使用一个线程计时器来做到这一点,该计时器将与在超时后杀死它的进程一起启动,但由于僵尸进程或类似问题,我确实遇到了一些陈旧的文件描述符问题。

回答by pyfunc

No there is no time out. I guess, what you are looking for is to kill the sub process after some time. Since you are able to signal the subprocess, you should be able to kill it too.

不,没有时间。我想,您正在寻找的是在一段时间后终止子进程。由于您能够向子进程发出信号,因此您也应该能够杀死它。

generic approach to sending a signal to subprocess:

向子进程发送信号的通用方法:

proc = subprocess.Popen([command])
time.sleep(1)
print 'signaling child'
sys.stdout.flush()
os.kill(proc.pid, signal.SIGUSR1)

You could use this mechanism to terminate after a time out period.

您可以使用此机制在超时后终止。

回答by Mike Graham

You could do

你可以做

from twisted.internet import reactor, protocol, error, defer

class DyingProcessProtocol(protocol.ProcessProtocol):
    def __init__(self, timeout):
        self.timeout = timeout

    def connectionMade(self):
        @defer.inlineCallbacks
        def killIfAlive():
            try:
                yield self.transport.signalProcess('KILL')
            except error.ProcessExitedAlready:
                pass

        d = reactor.callLater(self.timeout, killIfAlive)

reactor.spawnProcess(DyingProcessProtocol(20), ...)

using Twisted's asynchronous process API.

使用 Twisted 的异步进程 API。

回答by fja0568

For Linux, you can use a signal. This is platform dependent so another solution is required for Windows. It may work with Mac though.

对于 Linux,您可以使用信号。这是平台相关的,因此 Windows 需要另一个解决方案。不过它可能适用于 Mac。

def launch_cmd(cmd, timeout=0):
    '''Launch an external command

    It launchs the program redirecting the program's STDIO
    to a communication pipe, and appends those responses to
    a list.  Waits for the program to exit, then returns the
    ouput lines.

    Args:
        cmd: command Line of the external program to launch
        time: time to wait for the command to complete, 0 for indefinitely
    Returns:
        A list of the response lines from the program    
    '''

    import subprocess
    import signal

    class Alarm(Exception):
        pass

    def alarm_handler(signum, frame):
        raise Alarm

    lines = []

    if not launch_cmd.init:
        launch_cmd.init = True
        signal.signal(signal.SIGALRM, alarm_handler)

    p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
    signal.alarm(timeout)  # timeout sec

    try:
        for line in p.stdout:
            lines.append(line.rstrip())
        p.wait()
        signal.alarm(0)  # disable alarm
    except:
        print "launch_cmd taking too long!"
        p.kill()

    return lines        
launch_cmd.init = False

回答by Eric Leschinski

A python subprocess auto-timeout is not built in, so you're going to have to build your own.

没有内置python子进程自动超时,因此您将不得不构建自己的。

This works for me on Ubuntu 12.10 running python 2.7.3

这在运行 python 2.7.3 的 Ubuntu 12.10 上对我有用

Put this in a file called test.py

把它放在一个名为 test.py 的文件中

#!/usr/bin/python
import subprocess
import threading

class RunMyCmd(threading.Thread):
    def __init__(self, cmd, timeout):
        threading.Thread.__init__(self)
        self.cmd = cmd 
        self.timeout = timeout

    def run(self):
        self.p = subprocess.Popen(self.cmd)
        self.p.wait()

    def run_the_process(self):
        self.start()
        self.join(self.timeout)

        if self.is_alive():
            self.p.terminate()   #if your process needs a kill -9 to make 
                                 #it go away, use self.p.kill() here instead.

            self.join()

RunMyCmd(["sleep", "20"], 3).run_the_process()

Save it, and run it:

保存并运行它:

python test.py

The sleep 20command takes 20 seconds to complete. If it doesn't terminate in 3 seconds (it won't) then the process is terminated.

sleep 20命令需要 20 秒才能完成。如果它没有在 3 秒内终止(它不会),则该过程终止。

el@apollo:~$  python test.py 
el@apollo:~$ 

There is three seconds between when the process is run, and it is terminated.

进程运行和终止之间有三秒钟的时间。

回答by Blairg23

import subprocess, threading

class Command(object):
    def __init__(self, cmd):
        self.cmd = cmd
        self.process = None

    def run(self, timeout):
        def target():
            print 'Thread started'
            self.process = subprocess.Popen(self.cmd, shell=True)
            self.process.communicate()
            print 'Thread finished'

        thread = threading.Thread(target=target)
        thread.start()

        thread.join(timeout)
        if thread.is_alive():
            print 'Terminating process'
            self.process.terminate()
            thread.join()
        print self.process.returncode

command = Command("echo 'Process started'; sleep 2; echo 'Process finished'")
command.run(timeout=3)
command.run(timeout=1)

The output of this should be:

这个输出应该是:

Thread started
Process started
Process finished
Thread finished
0
Thread started
Process started
Terminating process
Thread finished
-15

where it can be seen that, in the first execution, the process finished correctly (return code 0), while the in the second one the process was terminated (return code -15).

可以看出,在第一次执行中,进程正确完成(返回代码 0),而在第二次执行中,进程被终止(返回代码 -15)。

I haven't tested in windows; but, aside from updating the example command, I think it should work since I haven't found in the documentation anything that says that thread.join or process.terminate is not supported.

我还没有在 Windows 中测试过;但是,除了更新示例命令之外,我认为它应该可以工作,因为我没有在文档中找到任何说明不支持 thread.join 或 process.terminate 的内容。

回答by Mike Graham

As of Python 3.3, there is also a timeoutargument to the blocking helper functions in the subprocess module.

从 Python 3.3 开始,timeoutsubprocess 模块中的阻塞辅助函数也有一个参数。

https://docs.python.org/3/library/subprocess.html

https://docs.python.org/3/library/subprocess.html

回答by Tim Savannah

Yes, https://pypi.python.org/pypi/python-subprocess2will extend the Popen module with two additional functions,

是的,https://pypi.python.org/pypi/python-subprocess2 将通过两个附加功能扩展 Popen 模块,

Popen.waitUpTo(timeout=seconds)

This will wait up to acertain number of seconds for the process to complete, otherwise return None

这将等待进程完成特定秒数,否则返回 None

also,

还,

Popen.waitOrTerminate

This will wait up to a point, and then call .terminate(), then .kill(), one orthe other or some combination of both, see docs for full details:

这将等待一个点,然后调用 .terminate(),然后调用 .kill(),一个或另一个或两者的某种组合,有关完整详细信息,请参阅文档:

http://htmlpreview.github.io/?https://github.com/kata198/python-subprocess2/blob/master/doc/subprocess2.html

http://htmlpreview.github.io/?https://github.com/kata198/python-subprocess2/blob/master/doc/subprocess2.html