为什么 stdin.readline 没有读到最后
Why stdin.readline doesn't read until the end
我是 python 的新手,我尝试从其他 python 脚本向一个子进程发送信息,但子进程在主进程停止发送之前不会读取任何内容。
我尝试发送新行和以“\n”结尾的行。
我知道我的子进程在流完成之前一直被阻塞,但是如果我发送 \n 或直接 stdin.write ('\n'),它应该正确读取,但它不会
主要流程:
import subprocess
import time
child = subprocess.Popen("python3 example.py", shell=True, stdin=subprocess.PIPE, universal_newlines=True)
s = "this is the message"
print("MAIN:The child pid is: " + str(child.pid))
for i in range(0, 5):
print("MAIN:iteration send:" + str(i))
msg = s + "-" + str(i) + "\n"
child.stdin.writelines(msg)
time.sleep(1)
child.kill()
子进程:
import time
from sys import stdin
while True:
try:
print("CHILD:before read")
s = stdin.readline()
print("CHILD:after read")
print("CHILD:message received is:" + s)
except EOFError as err:
print("M_READ_ERROR")
time.sleep(1)
我的输出是这样的
MAIN:The child pid is: 18041
MAIN:iteration send:0
CHILD:before read
MAIN:iteration send:1
MAIN:iteration send:2
MAIN:iteration send:3
MAIN:iteration send:4
CHILD:after read
CHILD:message received id:this is the message-0
但我希望是这样的:
MAIN:The child pid is: 18041
MAIN:iteration send:0
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-0
MAIN:iteration send:1
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-1
MAIN:iteration send:2
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-2
MAIN:iteration send:3
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-3
MAIN:iteration send:4
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-4
您的管道正在使用系统的默认缓冲区大小 (io.DEFAULT_BUFFER_SIZE
)。 reader 由于缓冲区尚未填满而被阻塞,因此流上没有指示可读取的指示。
要解决此问题,您需要控制缓冲。有两种方法。
首先,您可以在每次写入后进行显式刷新。
child.stdin.writelines(msg)
child.stdin.flush()
这有效地实现了行缓冲,但在您自己的代码中。
其次,您可以通过传递 bufsize
kwarg 在 Popen() 调用中 select 缓冲模式。大于 1 的正 bufsizes 设置该值的缓冲区大小,这意味着您的 reader 将在该缓冲区被填充时每隔一段时间获得就绪信号。但也有特殊情况:
- 0表示无缓冲,这样每次写入都会立即刷新;
- 1 表示 line-buffered,这是一种特殊情况,在 user-space 中,io 库扫描写入换行符并在它们之后刷新。
您可以传递 bufsize=1
以在换行后强制刷新。在 python3 中,这取决于 universal_newlines=True
,但你已经有了。
我是 python 的新手,我尝试从其他 python 脚本向一个子进程发送信息,但子进程在主进程停止发送之前不会读取任何内容。
我尝试发送新行和以“\n”结尾的行。 我知道我的子进程在流完成之前一直被阻塞,但是如果我发送 \n 或直接 stdin.write ('\n'),它应该正确读取,但它不会
主要流程:
import subprocess
import time
child = subprocess.Popen("python3 example.py", shell=True, stdin=subprocess.PIPE, universal_newlines=True)
s = "this is the message"
print("MAIN:The child pid is: " + str(child.pid))
for i in range(0, 5):
print("MAIN:iteration send:" + str(i))
msg = s + "-" + str(i) + "\n"
child.stdin.writelines(msg)
time.sleep(1)
child.kill()
子进程:
import time
from sys import stdin
while True:
try:
print("CHILD:before read")
s = stdin.readline()
print("CHILD:after read")
print("CHILD:message received is:" + s)
except EOFError as err:
print("M_READ_ERROR")
time.sleep(1)
我的输出是这样的
MAIN:The child pid is: 18041
MAIN:iteration send:0
CHILD:before read
MAIN:iteration send:1
MAIN:iteration send:2
MAIN:iteration send:3
MAIN:iteration send:4
CHILD:after read
CHILD:message received id:this is the message-0
但我希望是这样的:
MAIN:The child pid is: 18041
MAIN:iteration send:0
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-0
MAIN:iteration send:1
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-1
MAIN:iteration send:2
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-2
MAIN:iteration send:3
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-3
MAIN:iteration send:4
CHILD:before read
CHILD:after read
CHILD:message received id:this is the message-4
您的管道正在使用系统的默认缓冲区大小 (io.DEFAULT_BUFFER_SIZE
)。 reader 由于缓冲区尚未填满而被阻塞,因此流上没有指示可读取的指示。
要解决此问题,您需要控制缓冲。有两种方法。
首先,您可以在每次写入后进行显式刷新。
child.stdin.writelines(msg)
child.stdin.flush()
这有效地实现了行缓冲,但在您自己的代码中。
其次,您可以通过传递 bufsize
kwarg 在 Popen() 调用中 select 缓冲模式。大于 1 的正 bufsizes 设置该值的缓冲区大小,这意味着您的 reader 将在该缓冲区被填充时每隔一段时间获得就绪信号。但也有特殊情况:
- 0表示无缓冲,这样每次写入都会立即刷新;
- 1 表示 line-buffered,这是一种特殊情况,在 user-space 中,io 库扫描写入换行符并在它们之后刷新。
您可以传递 bufsize=1
以在换行后强制刷新。在 python3 中,这取决于 universal_newlines=True
,但你已经有了。