【问题标题】:python communicate() hangs after process terminate()python通信()在进程终止()后挂起
【发布时间】:2020-06-08 18:59:50
【问题描述】:

以下代码尝试并行运行多个命令,每个命令都有超时。如果超时未完成处理,则将停止(我正在使用终止())。

问题是在终止后(返回码设置为 -ve),communicate() 方法挂起,当强制退出 (Ctrl+C) 时会显示以下错误。

(stdout, stderr) = proc.communicate()
File "python3.7/subprocess.py", line 926, in communicate
stdout = self.stdout.read()

代码

procList = []
for app in appList:
    try:
        p = subprocess.Popen(app['command'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
        procList.append((app, p))
    except Exception as e:
        print(e)

start = time.time()
while len(procList):
    time.sleep(30)
    try:
        for app, proc in procList:
            if (time.time() - start > app['timeoutSec']):
                proc.terminate()
            if proc.poll() is not None and app['mailSent'] == 0:
                (stdout, stderr) = proc.communicate() #Hangs here is the process is terminated
                send_results_mail('Execution Completed or Terminated')
                app['mailSent'] = 1
    except subprocess.SubprocessError as e:
        print(e)
    procList = [(app, proc) for (app, proc) in procList if app['mailSent'] == 0]

【问题讨论】:

  • 我正在使用 Linux。

标签: python subprocess timeout terminate


【解决方案1】:

编辑:这是一个工作示例,使用 kill(),带有行为不端的孩子(不会响应 terminate()),但我不确定您的子进程的性质。希望这有助于您更接近解决方案!

简单的子程序,Python 3:

import signal
import time

def sighandler(signal, _stack):
    print(f"Ignoring signal {signal}")

signal.signal(signal.SIGTERM, sighandler)
signal.signal(signal.SIGINT, sighandler)

secs = 10
print(f"Sleeping {secs} seconds...")
time.sleep(secs)
print("Exiting...")

修改后的父程序,Python 3:

import subprocess
import time

start = time.time()
appList = [
   {'command': 'python ./child.py', 'timeoutSec': 2, 'mailSent': 0},
   {'command': 'python ./child.py', 'timeoutSec': 2, 'mailSent': 0},
   {'command': 'python ./child.py', 'timeoutSec': 2, 'mailSent': 0},
]

def logmsg(msg):
    elap = time.time()-start
    print(f"{elap:2.1f} secs: {msg}")

def send_results_mail(result):
    logmsg(f"Result: {result}")

procList = []
for app in appList:
    try:
        p = subprocess.Popen(app['command'], stdout=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
        procList.append((app, p))
    except Exception as e:
        logmsg(e)
    logmsg(f"Launching child... {p.pid}")

start = time.time()
while len(procList):
    time.sleep(1)
    try:
        for app, proc in procList:
            if (time.time() - start > app['timeoutSec']):
                logmsg(f"Trying to terminate()...{proc.pid}")
                proc.terminate()
                proc.kill()
            if proc.poll() is not None and app['mailSent'] == 0:
                proc.kill()
                logmsg(f"Trying to communicate()...{proc.pid}")
                (stdout, stderr) = proc.communicate()
                send_results_mail('Execution Completed or Terminated')
                app['mailSent'] = 1
    except subprocess.SubprocessError as e:
        logmsg(e)
    procList = [(app, proc) for (app, proc) in procList if app['mailSent'] == 0]

输出:

$ python parent.py
0.0 secs: Launching child... 537567
0.0 secs: Launching child... 537568
0.0 secs: Launching child... 537569
2.0 secs: Trying to terminate()...537567
2.0 secs: Trying to terminate()...537568
2.0 secs: Trying to terminate()...537569
3.0 secs: Trying to terminate()...537567
3.0 secs: Trying to communicate()...537567
3.0 secs: Result: Execution Completed or Terminated
3.0 secs: Trying to terminate()...537568
3.0 secs: Trying to communicate()...537568
3.0 secs: Result: Execution Completed or Terminated
3.0 secs: Trying to terminate()...537569
3.0 secs: Trying to communicate()...537569
3.0 secs: Result: Execution Completed or Terminated

【讨论】:

  • 感谢您的回复,您分享的示例是针对通信()超时而我正在处理该过程的超时。所以那个部分不能被删除。此外,添加超时会导致 while 循环结束,然后 proc.kill() 被执行,但它仍然没有终止进程。
  • 感谢您的反馈,@AnubhavBansal 我尝试调整响应以显示使用 kill() 的工作示例。我希望这可以帮助您更接近解决方案。
【解决方案2】:

感谢所有建议和回复,但对我来说,没有什么能解决问题,因为我所跨越的流程正在创建多个级别的子流程。

所以我不得不递归终止进程,为此我使用了psutil的解决方案

详情请看下面的帖子

https://stackoverflow.com/a/27034438/2393961

一旦所有子进程和孙子进程都被杀死communicate() 工作正常。

另外,我学到的一点是,尽管htop 可以向您展示进程的树形结构,但每个进程都是独立的,杀死父进程并不会自动杀死其死者。感谢我的朋友Noga 指出这一点

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-11-10
    • 2016-12-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-27
    • 2011-08-10
    • 2012-06-04
    相关资源
    最近更新 更多