【问题标题】:Alternatives to Python Popen.communicate() memory limitations?Python Popen.communicate() 内存限制的替代方案?
【发布时间】:2011-10-16 08:14:27
【问题描述】:

我有以下 Python 代码块(运行 v2.7),当我处理大型(几 GB)文件时会引发 MemoryError 异常:

myProcess = Popen(myCmd, shell=True, stdout=PIPE, stderr=PIPE)
myStdout, myStderr = myProcess.communicate()
sys.stdout.write(myStdout)
if myStderr:
    sys.stderr.write(myStderr)

在阅读documentation to Popen.communicate() 时,似乎有一些缓冲发生:

注意读取的数据是缓存在内存中的,所以如果数据量很大或者没有限制,不要使用这种方式。

有没有办法禁用这种缓冲,或者在进程运行时强制定期清除缓存?

我应该在 Python 中使用什么替代方法来运行将千兆字节数据流式传输到 stdout 的命令?

我应该注意我需要处理输出和错误流。

【问题讨论】:

  • 我需要能够流式输出和错误。

标签: python memory stream popen


【解决方案1】:

我想我找到了解决办法:

myProcess = Popen(myCmd, shell=True, stdout=PIPE, stderr=PIPE)
for ln in myProcess.stdout:
    sys.stdout.write(ln)
for ln in myProcess.stderr:
    sys.stderr.write(ln)

这似乎使我的内存使用量下降到足以完成任务。

更新

我最近发现了一种在 Python 中处理数据流的更灵活的方法,使用 threads。有趣的是,Python 在某些方面很差,以至于 shell 脚本可以轻松完成!

【讨论】:

  • 这看起来很有趣。我也试试看。
  • 这会忽略文档中的Warning: Use communicate() rather than .stdin.write, .stdout.read or .stderr.read to avoid deadlocks due to any of the other OS pipe buffers filling up and blocking the child process. 部分。它可能会正常工作,但如果myProcess.stderr 被填满,for ln in myProcess.stdout: 可能会出现死锁。我自己来这里寻找解决方案。
  • 顺便说一句,仅当 stdout 和 stderr 的大小大致相同时,使用 izip_longest() 才会有所帮助。如果一个在另一个之前用完,它将阻塞,剩下的一个将完全缓冲,直到过程结束。在这种情况下,内存使用量不会减少,实际上可能比使用.communicate() 更糟糕,因为如果左边的内部缓冲区填满,它会死锁。 (这个缓冲区通常比.communicate() 可以分配的要小得多。)至少在@Alex 的解决方案中,.stdout 可能包含更多数据。
  • @antak:为了避免死锁,如果 stdout/stderr 在子进程运行时都由 Python 代码处理;有几种异步技术可以提供帮助:threadsselectfcntl、带有 IOCP 的命名管道。
【解决方案2】:

如果我需要读取标准输出以获取这么大的内容,我可能会做的是在创建进程时将其发送到文件中。

with open(my_large_output_path, 'w') as fo:
    with open(my_large_error_path, 'w') as fe:
        myProcess = Popen(myCmd, shell=True, stdout=fo, stderr=fe)

编辑:如果您需要流式传输,您可以尝试制作一个类似文件的对象并将其传递给 stdout 和 stderr。 (不过,我还没有尝试过。)然后您可以在对象被写入时读取(查询)它。

【讨论】:

    【解决方案3】:

    对于那些在使用Popen时应用程序在一定时间后挂起的人,请在下面查找我的案例:

    经验法则,如果您不打算使用 stderrstdout 流,则不要传递/初始化它们Popen 的参数!因为它们会填满并给您带来很多问题。

    如果您需要它们一段时间并且需要保持进程运行,那么您可以随时关闭这些流。

    try:
        p = Popen(COMMAND, stdout=PIPE, stderr=PIPE)
        # After using stdout and stderr
        p.stdout.close()
        p.stderr.close()
    except Exception as e:
        pass
    

    【讨论】:

      猜你喜欢
      • 2010-09-24
      • 2020-05-24
      • 1970-01-01
      • 1970-01-01
      • 2019-06-20
      • 2014-10-10
      • 2016-02-19
      • 1970-01-01
      相关资源
      最近更新 更多