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

4

我有一个问题一直困扰着我,但似乎找不到解决办法。我一直使用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脚本等待子进程结束? - Some programmer dude
如果 process.stdin 被缓存,你的写入可能不会立即显示在 C++ 程序中。同样地,如果 process.stdout 被缓存,C++ 的写入也可能不会立即显示在你的 Python 程序中。 - chepner
谢谢您的回复,但我尝试添加了长度参数,但仍然没有输出。 - user1481671
如果您将print语句更改为print process.stdout.readline()会怎样?并确保您的C ++代码在打印到cout时附加换行符。 - Praetorian
尝试在while循环内使用条件“while process.returncode is not None:”将“read”替换为“readline”。 - SethMMorton
显示剩余3条评论
2个回答

3
请使用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,我尝试了一下,现在出现了这个错误:out,err = process.communicate(command) File "/usr/lib/python2.7/subprocess.py", line 806, in communicate return self._communicate(input) File "/usr/lib/python2.7/subprocess.py", line 1382, in _communicate stdout,stderr = self._communicate_with_poll(input) File "/usr/lib/python2.7/subprocess.py", line 1456, in _communicate_with_poll data = os.read(fd, 4096) MemoryError - user1481671
你的外部应用程序可能输出了大量的数据。我编辑了我的答案,给出了一个读取大量输出的示例。实际上,它更接近于你的原始代码。 - Teebrin

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接