【问题标题】:How to fix hanging in subprocess.check_output()如何修复挂在 subprocess.check_output()
【发布时间】:2019-09-02 04:34:05
【问题描述】:

我正在尝试执行以下代码以运行带有配置文件作为输入的 python 脚本:

try:
    process = check_output(["python", "E:/SpaceWeather/Source_codes/codes_written/tec-suite-master/tecs.py", \
                       "-c",str(cfgdirectory/'new.cfg')],stderr=STDOUT,timeout = 300)
except subprocess.CalledProcessError as e:
    print(e.output)

我是这样写的,因为我之前使用Popenprocess.communicate(),它一直挂着。我的脚本的设计方式使用不同的配置文件循环通过check_outputtimeout 参数旨在结束循环中需要太长时间的实例。但是,当我现在运行脚本时,有时它会随机挂起特定的循环实例,我不知道为什么。超时参数似乎没有结束进程并进入循环中的下一个迭代?

有人知道为什么会这样吗?

注意:我们输入配置文件的脚本E:/SpaceWeather/Source_codes/codes_written/tec-suite-master/tecs.py 与我的工作目录位于不同的位置,我不确定这是否会有所不同。

编辑:我尝试了在Subprocess timeout failure 看到的以下解决方案:

import os
import signal
from subprocess import Popen, PIPE, TimeoutExpired
from time import monotonic as timer

start = timer()
with Popen('sleep 30', shell=True, stdout=PIPE, preexec_fn=os.setsid) as process:
    try:
        output = process.communicate(timeout=1)[0]
    except TimeoutExpired:
        os.killpg(process.pid, signal.SIGINT) # send signal to the process group
        output = process.communicate()[0]

它似乎可以很好地运行循环。但是,在循环结束时,该过程不会结束并无限期地挂起。如何强制进程在循环结束时实际终止?

编辑 2:我编辑了一些行以说明在 Windows 平台上,并且所有似乎都运行正常,直到其中一个进程遇到超时。此后,一切都挂起。出于某种原因,即使进程执行了它们应该做的事情,它们也不会结束!

with Popen(["python", "E:/SpaceWeather/Source_codes/codes_written/tec-suite-master/tecs.py", \
                   "-c",str(cfgdirectory/'new.cfg')],stdout=PIPE \
                        ,shell=False) as process:
    try:   
        output = process.communicate(timeout=300)[0]

    except TimeoutExpired:
        print('Forced kill at ' + str(observationDateTime))
#        os.kill(process.pid, signal.CTRL_C_EVENT)
        subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
        output = process.communicate()[0]
        print('done')

    except Exception as e:
        print('UNKNOWN EXCEPTION, TERMINATED')
#        os.kill(process.pid, signal.CTRL_C_EVENT)
        subprocess.call(['taskkill', '/F', '/T', '/PID', str(p.pid)])
        output = process.communicate()[0]
        print('done')

在上面的代码中,当打印Forced kill at .... 时,应该遵循的done 永远不会出现。这可能是因为我正在循环处理进程,因此process 不断变化,因此难以终止它?

【问题讨论】:

  • 挂起时是否抛出 TimeoutExpired 异常? timeout 参数并不能保证结束子进程,您必须自己捕获 TimeoutExpired 并调用 kill()。但我没有找到如何使用 check_output 产生的进程来做到这一点
  • 我已经按照您推荐的方式尝试了一个解决方案,但是现在该过程不会在循环结束时终止,即使它超过了超时时间。有没有办法强制它终止?我在我的问题中添加了更多细节。

标签: python-3.x subprocess


【解决方案1】:

你在正确的轨道上,
只需将signal.SIGINT 更改为signal.SIGKILL

信号 SIGKILL 和 SIGSTOP 不能被捕获、阻止或忽略。

而 SIGINT 可以被捕获、阻止或忽略。

根据问题修改示例代码
这将在 2 秒内打印“完成”
如果用 SIGTERM 替换 SIGKILL 将打印“完成”长达 10 秒

import os
import signal
from subprocess import Popen, PIPE, TimeoutExpired
from time import monotonic as timer

start = timer()
with Popen('bash -c "trap \\\"\\\" 9 15; sleep 10;"', shell=True, stdout=PIPE, preexec_fn=os.setsid) as process:
    try:
        output = process.communicate(timeout=1)[0]
    except TimeoutExpired:
        print("forced kill")
        os.killpg(process.pid, signal.SIGKILL) # send signal to the process group
        output = process.communicate()[0]
        print("done")

【讨论】:

  • 我刚刚试了一下,它仍然挂起。我认为这是因为 TimeoutExpired 没有提高
猜你喜欢
  • 2012-02-15
  • 1970-01-01
  • 2011-12-05
  • 2014-10-23
  • 2017-05-21
  • 1970-01-01
  • 2012-12-14
  • 2016-12-14
相关资源
最近更新 更多