子进程冻结 popen().stdout.read

Posted

技术标签:

【中文标题】子进程冻结 popen().stdout.read【英文标题】:Subprocess freezes popen().stdout.read 【发布时间】:2013-06-19 18:16:32 【问题描述】:

这个问题困扰了我一段时间,我似乎找不到解决方案,我一直在使用 subprocess.Popen() 来访问一个 C++ 应用程序,该应用程序为我做了一些繁重的计算,但它一直在 Popen().stdout.read(); 这是python代码:

process = subprocess.Popen(['/path/to/my/executable'], shell=False, 
stdout=subprocess.PIPE, stdin=subprocess.PIPE)
process.stdin.write("Some String")
print process.stdout.read()#It freezes here

这是 c++ 代码:

int main(int argc, char** argv) 
    ...Prep work...
    while (1) 
        string input;
        cin>>input;
    ...Some Work ...
        cout<< response;
    

c++ 代码在 shell 中完美运行,但我不知道为什么它在 python 上冻结

【问题讨论】:

因为Python脚本等待子进程结束? 我可以在不等待进程结束的情况下读取 cout 吗? 如果 process.stdin 被缓冲,您的写入可能不会立即对 C++ 程序可见。同样,如果 process.stdout 被缓冲,C++ 写入可能不会立即对您的 Python 程序可见。 您应该能够将长度参数传递给read(),否则您将等待一个EOF,在子流程完成之前您可能不会有。 感谢您的回复,但我尝试添加长度参数但仍然没有输出 【参考方案1】:

改用communicate()

import subprocess
process = subprocess.Popen(['app'], shell=False,
                           stdout=subprocess.PIPE,
                           stdin=subprocess.PIPE)
out, err = process.communicate("Some String")
print out

另外,请确保您在某个时间点结束您的 C++ 进程。例如,当您到达输入流的末尾时:

#include <string>
#include <iostream>
using namespace std;

int main(int argc, char** argv) 
    //...Prep work...
    while (cin)   // <-- Will eventually reach the end of the input stream
        string input;
        cin >> input;
        //...Some Work ...
        string response = input;
        cout << response;
    

在 python 的文档中有一个警告: http://docs.python.org/2/library/subprocess.html#subprocess.Popen.stdin(正上方)

它说明当您写入外部应用程序时,数据可能会放入队列中。此外,您的外部应用程序的输出也可能会放入队列中。 communicate() 将“刷新”您发送到外部应用程序的内容并等到您的应用程序终止。

使用communicate() 将在内存中获取整个外部应用程序的输出。如果它不实用(例如巨大的输出),那么您可以使用 stdin 和 stdout 对象进行写入或读取。你需要注意不要“死锁”:

import subprocess

process = subprocess.Popen(['app'], shell=False,
                           stdout=subprocess.PIPE,
                           stdin=subprocess.PIPE)
process.stdin.write("Some String")
process.stdin.close()  # <-- Makes sure the external app gets an EOF while
                       #     reading its input stream.
for line in process.stdout.readlines():
    print line

但即使使用这种技术,也要确保您提供给外部应用程序的输入足够小,以避免在写入时阻塞。

如果您的输入也很大,则必须确保您的读写没有阻塞。那么使用线程很可能是一个不错的选择。

【讨论】:

感谢您的回复@Teebrin,我试过了,现在我得到了这个错误,err = process.communicate(command) File "/usr/lib/python2.7/subprocess. py”,第 806 行,在通信中返回 self._communicate(input) 文件“/usr/lib/python2.7/subprocess.py”,第 1382 行,在 _communicate 标准输出中,stderr = self._communicate_with_poll(input) 文件“/usr /lib/python2.7/subprocess.py",第 1456 行,在 _communicate_with_poll 数据 = os.read(fd, 4096) MemoryError 您的外部应用程序可能正在输出大量数据。我编辑了我的答案,以举例说明如何阅读大量输出。事实上,它更像你的原始代码。【参考方案2】:

通常我们需要非阻塞 IO, 1)读取所有回复,直到没有更多数据,然后 2)向子进程发出一些东西, 重复 1-2 使用线程也会有所帮助。

【讨论】:

以上是关于子进程冻结 popen().stdout.read的主要内容,如果未能解决你的问题,请参考以下文章

subprocess模块

有人可以解释管道缓冲区死锁吗?

python web服务器学习笔记 并发尝试之popen原理探究

python Popen卡死问题

终止正在运行的子进程调用

如何使用子进程popen Python [重复]