如何将 STDIN 两次发送到 Popen 进程,每次都使用 EOF?

How to send STDIN twice to Popen process, each time with EOF?

我有这部分代码:

for stdin in stdins:
    p.stdin.write(stdin)

写入字符串 stdin 以处理 p 的 STDIN。

挑战在于:进程p希望在进入下一个 STDIN 之前看到 EOF。

对于上面的循环,问题是后续的 p.stdin.write(stdin) 将被进程 p 视为第一个 STDIN 输入集合的输入。因为,如前所述,p 期望在移动到后续字段之前看到 EOF。

所以,我的问题是:如何在Python中解决这个问题?该过程需要看到如下内容:

for stdin in stdins:
    p.stdin.write(stdin)
    p.stdin.send_eof()

限制条件:解决方案不得使用 pexpect。

EOF不是一个字符,它只是表示没有更多数据可读

因此,我不相信您所追求的在 Python 或大多数其他语言中是可能的。

当我尝试在 python 中使用多个子进程进行异步渲染时遇到了同样的问题,这些子进程需要与主进程进行低延迟通信。

当我使用subprocess.popen()stdin=subprocess.PIPE时,我发现子进程无法获取任何内容,直到stdin.close()发生或主进程退出,都发送一个EOF信号但是使 PIPE 一次性使用。当然,我尝试了 stdin.writelines()stdin.flush()pickle.dump() 等,但其中 none 成功了。

但是有一种方法可以使用 NumPy.

与子进程重复通信

ndarray.tofile可以直接把一个数组传给一个文件对象。虽然文档声明等价于file.write(a.tobytes()),但确实有道理。我很困惑,直到我在文档页面的末尾阅读了这篇文章:

When fid is a file object, array contents are directly written to the file, bypassing the file object’s write method. As a result, tofile cannot be used with files objects supporting compression (e.g., GzipFile) or file-like objects that do not support fileno() (e.g., BytesIO).

其实我觉得是file.write()的错。任何调用 write() 方法的函数都不可避免地无法发送 EOF,除非我们绕过 write() 方法,如果不使用像 NumPy 这样的 C 扩展,这是不可能的。

通过 PIPE 发送一般数据现在有两种方式:

  1. NumPy 支持dtype=object,这意味着您可以直接将消息打包到对象数组中。另见 numpy.lib.format.

    Stores object arrays, i.e. arrays containing elements that are arbitrary Python objects. Files with object arrays are not to be mmapable, but can be read and written to disk.

  2. 如果消息具有规则模式,您可以将 Struct 声明为 dtype 来打包消息,这就是我的情况。这是我的例子。

    task = np.dtype([(  "index",  np.uint8         ),
                     (   "text",  np.unicode_, 128 ),
                     (  "color",  np.uint8,    2   ),
                     (   "size",  np.uint8         )])
    for i in range(123):
        np.empty(1, dtype=task).tofile(s.stdin)  # s is the subprocess' name.
        time.sleep(1)
    

    然后我分别成功获取了123次子进程中的消息

    真心希望能帮到你。因为我花了将近 4 天的时间才找到这个解决方案。我几乎在考虑使用磁盘上的真实文件来完成进程之间的通信——这应该会更慢——但是感谢 NumPy,我的调试终于告一段落了...


此外,我认为 np.save() 发送 EOF 毫无意义。您可以在 python 控制台中尝试此操作。

>>> import numpy as np
>>> import sys
>>> a = np.arange(100).reshape(10,10)
>>> a.tofile(sys.stdout.buffer)
... some garbled characters ...
>>> a.tofiler(sys.stdout)
... some garbled characters ...
>>> np.save(sys.stdout.buffer, a)
... some garbled characters ...
>>> np.save(sys.stdout, a)
Traceback (most recent call last):
  File "<input>", line 1, in <module>
  File "<__array_function__ internals>", line 5, in save
  ...
TypeError: write() argument must be str, not bytes

原因是sys.stdout.buffer.write()接受bytes参数,而sys.stdout.write()接受str。所以使用 array.tofile 写 sys.stdout 没有导致任何错误表明它没有调用 write() 方法而 np.save() 调用了。这引发了一个问题,似乎 np.fromfile 不支持 dtype=object 模式。对此感到抱歉。也许通过管道通过进程传输动态类型数据真的很难,但我听说在 ctype 模块中有一些方法可以在进程之间共享 RAM,这可能会有所帮助。

提到我在终端运行上面的脚本失败(io.UnsupportedOperation: seek) ,但它 运行 在 PyCharm 的 python 控制台中运行良好。我对此一无所知。也许 PyCharm 的 python 控制台实际上也有 sys.stdin 的代理。

另外,subprocess.PIPE 似乎有最大缓冲区大小,因此无法传输渲染图像。作为我的实验结果,将它们分成块并没有帮助。