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

Python 需要避免子进程死锁而不进行通信,python,subprocess,pipe,stdout,deadlock,Python,Subprocess,Pipe,Stdout,Deadlock,我需要一个execute命令,该命令产生大量的输出,并且需要花费大量的时间来执行(>30分钟)。我正在考虑使用subprocess.Popen来做这件事。我需要捕获命令的输出,因此我将管道传递给stdout和stderr 使用Popen.wait()时出现的死锁问题在许多论坛上都有很好的记录,因此建议使用Popen.communicate()来避免死锁。该解决方案的问题是communicate()会一直阻塞,直到命令完成。我需要在执行命令时打印所有到达stdout的内容。如果20分钟后没有输出,

我需要一个execute命令,该命令产生大量的输出,并且需要花费大量的时间来执行(>30分钟)。我正在考虑使用subprocess.Popen来做这件事。我需要捕获命令的输出,因此我将管道传递给stdout和stderr

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

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

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

有办法吗?

您尝试过吗?

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


这个问题解决了Windows和Linux的一些方法:

,您可以考虑使用多个线程。分配一个线程从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)
while time.time()-上次输出时间<20*60:
time.sleep(20*60-(time.time()-上次输出时间))
打印“在过去20分钟内未检测到输出”。终止执行'
系统出口(1)

为了避免管道缓冲区被填满,只需在父进程中启动一个后台线程。该线程可以连续读取stdout(和stderr)以防止管道缓冲区填满,也可以从中调用
communicate()
。无论哪种方式,主线程都可以自由地继续进行普通处理,并且子进程不会阻塞输出操作

将同步IO操作转换为异步IO操作(从主线程的角度来看)是线程的最佳用例之一。即使像Twisted这样的异步框架有时也会在给定操作没有本机异步接口时将其用作最后的解决方案。

  • 要解决Python2.4的问题,您可以将
    stdin
    连接到
    NUL
    设备


不幸的是,pexpect只能在类Unix系统上工作。我需要一个跨平台的解决方案。这正是我所需要的。谢谢!你刚刚解决了一个问题,我花了整整一个工作日来调查@GDICommander:小心,代码可能会将stderr放在下面。它在Ubuntu上运行良好。确保在Windows上测试它。如果要发送输入?示例:p.stdin.write(“YES”)@baybora.oren只需设置stdin=PIPE。虽然通常情况下,如果您希望同时发送输入和接收输出,那么您应该非常小心,以避免由于任何OS管道缓冲区填满而导致死锁:确保另一方在写入时读取,相反:线程、异步IO通常会解决此问题。此外,pexpect解决了块缓冲问题和stdin/stdout之外的输入/输出问题。(来自google?)当其中一个管道的缓冲区被填满而不被读取时,所有管道都会死锁。e、 g.填充stderr时,stdout死锁。不要通过你不想读的烟斗。
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()