我正在使用子进程模块来启动一个子进程并连接到它的输出流(标准输出)。我想能够在其stdout上执行非阻塞读取。有没有办法让.readline非阻塞或在我调用之前检查流上是否有数据.readline?我希望这是可移植的,或者至少在Windows和Linux下工作。
这里是我现在怎么做(.readline如果没有数据是可用的,它是阻塞的):
p = subprocess.Popen('myprogram.exe', stdout = subprocess.PIPE)
output_str = p.stdout.readline()
经常遇到类似的问题,我经常编写的Python程序需要能够执行一些主要功能,同时接受来自命令行(stdin)的用户输入。简单地把用户输入处理功能放在另一个线程中并不能解决问题,因为readline()块没有超时。如果主要功能已经完成,并且不再需要等待进一步的用户输入,我通常希望我的程序退出,但是它不能因为readline()在另一个线程中等待一条线路而阻塞。我发现这个问题的一个解决办法是使stdin成为一个非阻塞的文件,使用fcntl模块:
import fcntl
import os
import sys
# make stdin a non-blocking file
fd = sys.stdin.fileno()
fl = fcntl.fcntl(fd, fcntl.F_GETFL)
fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK)
# user input handling thread
while mainThreadIsRunning:
try: input = sys.stdin.readline()
except: continue
handleInput(input)
在我看来,这比使用选择或信号模块来解决这个问题稍微清洁一些,但它只能在UNIX上工作...
无论操作系统如何,无阻塞地读取流的可靠方法是使用Queue.get_nowait():
import sys
from subprocess import PIPE, Popen
from threading import Thread
try:
from Queue import Queue, Empty
except ImportError:
from queue import Queue, Empty # python 3.x
ON_POSIX = 'posix' in sys.builtin_module_names
def enqueue_output(out, queue):
for line in iter(out.readline, b''):
queue.put(line)
out.close()
p = Popen(['myprogram.exe'], stdout=PIPE, bufsize=1, close_fds=ON_POSIX)
q = Queue()
t = Thread(target=enqueue_output, args=(p.stdout, q))
t.daemon = True # thread dies with the program
t.start()
# ... do other things here
# read line without blocking
try: line = q.get_nowait() # or q.get(timeout=.1)
except Empty:
print('no output yet')
else: # got line
# ... do something with line