Python:如何查看pty对象以避免阻塞?
我正在使用 pty
来非阻塞地读取一个进程的标准输出,代码大致是这样的:
import os
import pty
import subprocess
master, slave = pty.openpty()
p = subprocess.Popen(cmd, stdout = slave)
stdout = os.fdopen(master)
while True:
if p.poll() != None:
break
print stdout.readline()
stdout.close()
一切运行得很好,除了有时候 while-loop
会卡住。这是因为 print stdout.readline()
这一行在等着从 stdout
读取数据。但是如果程序已经结束了,我的这个小脚本就会一直卡在那里。
我的问题是:有没有办法先查看一下 stdout
对象,看看有没有数据可以读取?如果没有数据,就应该继续执行 while-loop
,这样它就能发现进程其实已经结束了,然后跳出循环。
2 个回答
2
这个select.poll()的回答很不错,但在Windows上不适用。下面这个解决方案是个替代方案。虽然它不能让你查看标准输出,但提供了一种非阻塞的方式来替代readline(),并且是基于这个回答的:
from subprocess import Popen, PIPE
from threading import Thread
def process_output(myprocess): #output-consuming thread
nextline = None
buf = ''
while True:
#--- extract line using read(1)
out = myprocess.stdout.read(1)
if out == '' and myprocess.poll() != None: break
if out != '':
buf += out
if out == '\n':
nextline = buf
buf = ''
if not nextline: continue
line = nextline
nextline = None
#--- do whatever you want with line here
print 'Line is:', line
myprocess.stdout.close()
myprocess = Popen('myprogram.exe', stdout=PIPE) #output-producing process
p1 = Thread(target=process_output, args=(myprocess,)) #output-consuming thread
p1.daemon = True
p1.start()
#--- do whatever here and then kill process and thread if needed
if myprocess.poll() == None: #kill process; will automatically stop thread
myprocess.kill()
myprocess.wait()
if p1 and p1.is_alive(): #wait for thread to finish
p1.join()
还有其他非阻塞读取的解决方案在这里被提出过,但对我来说都不管用:
- 那些需要使用readline的解决方案(包括基于队列的)总是会阻塞。要终止执行readline的线程很困难(甚至是不可能的)。这个线程只有在创建它的进程结束时才会被杀掉,而不是在产生输出的进程被杀掉时。
- 将低级的fcntl和高级的readline调用混合使用,可能会出现问题,正如anonnn所指出的那样。
- 使用select.poll()很不错,但根据Python文档,它在Windows上不适用。
- 使用第三方库似乎对这个任务来说有点过于复杂,还增加了额外的依赖。
11
是的,可以使用 select模块中的poll:
import select
q = select.poll()
q.register(stdout,select.POLLIN)
然后在循环中使用:
l = q.poll(0)
if not l:
pass # no input
else:
pass # there is some input