2017-03-02 49 views
2

我试图复制生成的命令变得无响应并且read()调用开始阻塞时在subprocess.Popen(command, stdout=PIPE)对象的stdout上发生的情况。这是所需的行为:一旦读取完所有数据,创建管道,写入管道,读取管道,任何进一步的读取都将被阻塞。我已经证实了这部作品在REPL:管道没有通过所有数据

>>> import os 
>>> os.pipe() 
(3, 4) 
>>> a = os.fdopen(3) 
>>> b = os.fdopen(4, 'w') 
>>> b.write("line 1\n" 
...   "line 2\n" 
...   "line 3\n" 
...   "line 4\n" 
...   "line 5\n") 
>>> b.flush() 
>>> a.readline() 
'line 1\n' 
>>> a.readline() 
'line 2\n' 
>>> a.readline() 
'line 3\n' 
>>> a.readline() 
'line 4\n' 
>>> a.readline() 
'line 5\n' 
>>> a.readline() 
    ## hangs here forever... 

然而,看似在下面的代码相同类型的事情只产生输出的第一行的读取被阻塞其余部分之前。我究竟做错了什么??

代码:

import os 
import select 


class Stuck(): 
    def __init__(self): 
     pipe_readfd, pipe_writefd = os.pipe() 
     self.output = os.fdopen(pipe_readfd, 'r') 

     self.writer = os.fdopen(pipe_writefd, 'w') 
     self.writer.write('output line 1\n' 
          'output line 2\n' 
          'output line 3\n' 
          'output line 4\n' 
          'output line 5\n') 
     self.writer.flush() 


class Runner(): 
    def run(self, timeout=3): 
      stuck = Stuck() 
      opendescriptors = [stuck.output] 
      timeout_counter = 0 
      while opendescriptors: 
       pending = select.select(opendescriptors, [], [], 1)[0] 
       if not pending: 
        timeout_counter += 1 
        if timeout_counter >= timeout: 
         print "*** STUCK FOR OVER {} SECONDS ***\n".format(timeout) 
         return False 
       else: 
        timeout_counter = 0 
        for fd in pending: 
         line = fd.readline() 
         if line == '': 
          opendescriptors.remove(fd) 
         else: 
          print line 


def test(): 
    runner = Runner() 
    runner.run() 
    print "Expecting five lines of output followed by TIMEOUT" 


if __name__ == "__main__": 
    test() 

产生以下:

output line 1 

*** STUCK FOR OVER 3 SECONDS *** 

Expecting five lines of output followed by TIMEOUT 
+0

select非常以操作系统为中心。你在使用哪种操作系统? –

+0

我在OS X和Linux上看到相同的行为。 “选择”方面似乎是一致的,我没有理由认为这是行为不端,如果读取将被阻止,选择不返回描述符。我认为这个问题更多的是为什么读取仅在第一行之后开始阻塞,而不是在所有五行之后...... –

+0

如果flush()更改为close(),则获得全部五行输出。如果管道的写入端没有关闭,其他四个端口发生了什么? –

回答

1

据我所知,readline()做了很多的东西,没有发挥好与select()引擎盖下。用os.read()代替readline()会导致正确的行为。使用线程而不是select()是另一条路线。