Обратите внимание, что внутренне file.readlines([size])
зацикливается и вызывает системный вызов read()
более одного раза, пытаясь заполнить внутренний буфер size
. Первый вызов read()
немедленно вернется, так как select () указал, что fd был читаемым. Однако 2-й вызов будет блокироваться до тех пор, пока не станут доступны данные, что лишает смысла использование select. В любом случае сложно использовать file.readlines([size])
в асинхронном приложении.
Вы должны вызывать os.read(fd, size)
один раз для каждого fd для каждого прохода через select. Это выполняет неблокирующее чтение и позволяет буферизовать частичные строки до тех пор, пока данные не станут доступны, и однозначно обнаруживает EOF.
Я изменил ваш код, чтобы проиллюстрировать, используя os.read
. Он также читает из процесса 'stderr
:
import os
import select
import subprocess
from cStringIO import StringIO
target = 'Engine'
PIPE = subprocess.PIPE
engine = subprocess.Popen(target, bufsize=0, stdin=PIPE, stdout=PIPE, stderr=PIPE)
engine.stdin.write(b"go\n")
engine.stdin.flush()
class LineReader(object):
def __init__(self, fd):
self._fd = fd
self._buf = ''
def fileno(self):
return self._fd
def readlines(self):
data = os.read(self._fd, 4096)
if not data:
# EOF
return None
self._buf += data
if '\n' not in data:
return []
tmp = self._buf.split('\n')
lines, self._buf = tmp[:-1], tmp[-1]
return lines
proc_stdout = LineReader(engine.stdout.fileno())
proc_stderr = LineReader(engine.stderr.fileno())
readable = [proc_stdout, proc_stderr]
while readable:
ready = select.select(readable, [], [], 10.0)[0]
if not ready:
continue
for stream in ready:
lines = stream.readlines()
if lines is None:
# got EOF on this stream
readable.remove(stream)
continue
for line in lines:
print line