Ответ 1
Обратите внимание, что внутри file.readlines([size])
выполняется цикл и вызывает одноименный вызов read()
более одного раза, пытаясь заполнить внутренний буфер size
. Первый вызов read()
будет немедленно возвращен, так как select() указал, что fd читается. Однако 2-й вызов блокируется до тех пор, пока не будут доступны данные, что приведет к победе над целью использования выбора. В любом случае сложно использовать 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