需要避免子进程死锁而不进行通信

时间:2011-04-07 14:43:21

标签: python subprocess pipe stdout deadlock

我需要执行一个产生大量输出的命令,并且需要花费大量时间来执行(> 30分钟)。我正在考虑使用subprocess.Popen来做到这一点。我需要捕获命令的输出,所以我将PIPE传递给stdout和stderr。

使用Popen.wait()时的死锁问题已在很多论坛上得到充分记录,因此Popen.communicate()是避免死锁的建议方法。该解决方案的问题是communication()阻塞直到命令完成。我需要在执行命令时打印到达stdout的所有内容。如果20分钟后没有输出,脚本执行将被终止。

以下是我需要遵守的一些限制因素:

  • 我的Python版本是2.4.2,我无法升级。
  • 如果解决方案仍然使用子进程,我需要将subprocess.PIPE传递给所有std句柄以避免此错误:http://bugs.python.org/issue1124861

有办法吗?

5 个答案:

答案 0 :(得分:12)

import os
from subprocess import PIPE, STDOUT, Popen

lines = []
p = Popen(cmd, bufsize=1, stdin=open(os.devnull), stdout=PIPE, stderr=STDOUT)
for line in iter(p.stdout.readline, ''):
      print line,          # print to stdout immediately
      lines.append(line)   # capture for later
p.stdout.close()
p.wait()

答案 1 :(得分:6)

您是否尝试过pexpect

答案 2 :(得分:3)

听起来你需要对连接到管道的文件句柄进行非阻塞读取。

这个问题解决了Windows和Windows的一些方法。 linux:Non-blocking read on a subprocess.PIPE in python

答案 3 :(得分:1)

要避免管道缓冲区填满,只需在父进程中启动后台线程。该线程可以直接从stdout(和stderr)读取以保持管道缓冲区不会填满,或者您可以从中调用communicate()。无论哪种方式,主线程都可以继续进行普通处理,并且子进程不会阻止输出操作。

将同步IO操作转换为异步操作(从主线程的角度来看)是线程的最佳用例之一。甚至像Twisted这样的异步框架有时会将它作为最后的解决方案,当没有本地异步接口可用于给定操作时。

答案 4 :(得分:0)

您可以考虑使用多个线程。分配一个线程从stdout读取,一个来自stderr,并使用第三个线程来检测超时:

while time.time() - last_output_time < 20 * 60:
    time.sleep( 20 * 60 - (time.time() - last_output_time) )
print 'No output detected in the last 20 minutes. Terminating execution'
sys.exit(1)