不关闭子进程,重复向其标准输入写入并读取标准输出

13

我正在尝试在Python中使用Subprocess来以类似服务器的方式保持外部脚本的运行。外部脚本首先会加载一个模型,一旦加载完成,它就会通过STDIN接受请求并将处理后的字符串返回到STDOUT。

目前为止,我已经尝试过:

tokenizer = subprocess.Popen([tokenizer_path, '-l', lang_prefix], stdin=subprocess.PIPE, stdout=subprocess.PIPE)

然而,我无法使用

tokenizer.stdin.write(input_string+'\n')
out = self._tokenizer.stdout.readline()

通过子进程反复处理输入字符串,如果我使用stdout.read()或者stdout.readline(),输出将会为空。然而,当我在读取STDOUT之前使用tokenizer.stdin.close()关闭STDIN时,它可以正常工作,但这会关闭子进程,这不是我想要的,因为在发送另一个请求之前,我必须重新加载整个外部脚本。

是否有一种方法可以在Python中以类似于服务器的方式使用子进程,而无需关闭和重新打开它?


尝试在写入后刷新stdin,使用tokenizer.stdin.flush() - onon15
我已经尝试过了 - 不幸的是没有成功... - sam
你可能需要一个额外的线程来使它工作。检查subprocess.Popen.communicate的实现以了解如何操作。 - Fred Foo
我曾经做过类似的事情,其中我有一个守护进程通过命名管道与我的主程序通信。这个方法很有效。你考虑使用命名管道吗?我很乐意分享我的实现。 - Sheena
@larsmans:我之前检查过.communicate()的实现;你能给我一个提示,你所说的“使用额外线程”是什么意思吗? - sam
@Sheena:确实非常有帮助。你可以把它发布到pastebin.com或类似的网站上吗? - sam
2个回答

7

感谢这个答案,我发现必须使用从属句柄才能与子进程正常通信:

master, slave = pty.openpty()
tokenizer = subprocess.Popen(script, shell=True stdin=subprocess.PIPE, stdout=slave)
stdin_handle = process.stdin
stdout_handle = os.fdopen(master)

现在,我可以通过以下方式与子进程进行通信而无需关闭它:
stdin_handle.write(input)
stdout_handle.readline() #gets the processed input

2
你的外部脚本可能会缓冲其输出,因此只有在子进程中的缓冲区被刷新(子进程必须自己执行)时,才能在父进程中读取它。让它刷新其缓冲区的一种方法可能是关闭输入,因为这样它就可以以适当的方式终止并在过程中刷新其缓冲区。
如果你可以控制外部程序(即如果你可以打补丁),则在输出产生后插入一个刷新操作。
否则,有时可以通过将它们附加到伪终端上来使程序不缓冲其输出(包括stdlib在内的许多程序假定当它们的输出将要发送到TTY时,不希望进行缓冲)。但这有点棘手。

我其实刚刚找到了一个解决方案,使用Python的伪终端工具,感谢这个答案。 - sam
我仍然更喜欢显式刷新协程缓冲区。 - Alfe

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