Python子进程超时终止

Python subprocess kill with timeout(Python子进程超时终止)

本文介绍了Python子进程超时终止的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在 python 中使用 subprocess 模块运行一些 shell 脚本.如果 shell 脚本运行时间过长,我喜欢终止子进程.我认为如果我将 timeout=30 传递给我的 run(..) 语句就足够了.

I am running some shell scripts with the subprocess module in python. If the shell scripts is running to long, I like to kill the subprocess. I thought it will be enough if I am passing the timeout=30 to my run(..) statement.

代码如下:

try:
    result=run(['utilities/shell_scripts/{0} {1} {2}'.format(
                        self.language_conf[key][1], self.proc_dir, config.main_file)],
                shell=True,
                check=True,
                stdout=PIPE,
                stderr=PIPE, 
                universal_newlines=True, 
                timeout=30,
                bufsize=100)
except TimeoutExpired as timeout:

我已经用一些运行 120 秒的 shell 脚本测试了这个调用.我预计子进程会在 30 秒后被杀死,但实际上该进程正在完成 120 秒脚本,然后引发超时异常.现在的问题是如何通过超时杀死子进程?

I have tested this call with some shell scripts that runs 120s. I expected the subprocess to be killed after 30s, but in fact the process is finishing the 120s script and than raises the Timeout Exception. Now the Question how can I kill the subprocess by timeout?

推荐答案

文档明确指出应该终止进程:

The documentation explicitly states that the process should be killed:

来自 subprocess.run 的文档:

from the docs for subprocess.run:

将超时参数传递给Popen.communicate().如果超时,子进程将被杀死并等待.子进程终止后将重新引发TimeoutExpired异常."

"The timeout argument is passed to Popen.communicate(). If the timeout expires, the child process will be killed and waited for. The TimeoutExpired exception will be re-raised after the child process has terminated."

但在您的情况下,您使用的是 shell=True,我之前也看到过类似的问题,因为阻塞进程是 shell 进程的子进程.

But in your case you're using shell=True, and I've seen issues like that before, because the blocking process is a child of the shell process.

如果您正确分解参数并且您的脚本具有正确的 shebang,我认为您不需要 shell=True.你可以试试这个:

I don't think you need shell=True if you decompose your arguments properly and your scripts have the proper shebang. You could try this:

result=run(
  [os.path.join('utilities/shell_scripts',self.language_conf[key][1]), self.proc_dir, config.main_file],  # don't compose argument line yourself
            shell=False,  # no shell wrapper
            check=True,
            stdout=PIPE,
            stderr=PIPE, 
            universal_newlines=True, 
            timeout=30,
            bufsize=100)

请注意,我可以很容易地在 Windows 上重现此问题(使用 Popen,但它是同一件事):

note that I can reproduce this issue very easily on Windows (using Popen, but it's the same thing):

import subprocess,time

p=subprocess.Popen("notepad",shell=True)
time.sleep(1)
p.kill()

=> 记事本保持打开状态,可能是因为它设法与父 shell 进程分离.

=> notepad stays open, probably because it manages to detach from the parent shell process.

import subprocess,time

p=subprocess.Popen("notepad",shell=False)
time.sleep(1)
p.kill()

=> 记事本在 1 秒后关闭

=> notepad closes after 1 second

有趣的是,如果你删除 time.sleep()kill() 甚至可以使用 shell=True 可能是因为它成功杀死了正在启动 notepad.

Funnily enough, if you remove time.sleep(), kill() works even with shell=True probably because it successfully kills the shell which is launching notepad.

我并不是说您有完全相同的问题,我只是证明 shell=True 出于多种原因是邪恶的,并且无法终止/超时该进程是另一个原因原因.

I'm not saying you have exactly the same issue, I'm just demonstrating that shell=True is evil for many reasons, and not being able to kill/timeout the process is one more reason.

但是,如果你需要shell=True是有原因的,你可以使用psutil最后杀死所有的孩子.在这种情况下,最好使用 Popen 直接获取进程 ID:

However, if you need shell=True for a reason, you can use psutil to kill all the children in the end. In that case, it's better to use Popen so you get the process id directly:

import subprocess,time,psutil

parent=subprocess.Popen("notepad",shell=True)
for _ in range(30): # 30 seconds
    if parent.poll() is not None:  # process just ended
      break
    time.sleep(1)
else:
   # the for loop ended without break: timeout
   parent = psutil.Process(parent.pid)
   for child in parent.children(recursive=True):  # or parent.children() for recursive=False
       child.kill()
   parent.kill()

(来源:如何杀死进程和子进程蟒蛇?)

该示例也杀死了记事本实例.

that example kills the notepad instance as well.

这篇关于Python子进程超时终止的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:Python子进程超时终止

基础教程推荐