【问题标题】:Reliable non blocking reads from subprocess stdout从子进程标准输出可靠的非阻塞读取
【发布时间】:2014-03-29 10:48:18
【问题描述】:

注意:我有一个进程向 stdout ("print("hello")) 写入一行并等待 raw_input。我使用 p = subprocess.Popen 运行此进程,然后调用 p.stdout.readline()。 ...这会无限期地阻塞。我正在设置 shell=False...为什么我不能读取第一个输出?

p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin = subprocess.PIPE)
    print(p.stdout.readline())

我已经看到this 线程关于子进程的非阻塞 io。我正在尝试创建一个读取和写入进程的交互式子进程对象。我发现标准输出被阻塞,即使有输出发送到标准输出。为了测试这一点,我编写了一个程序,它将递增的整数输出到标准输出,并通过子进程调用它。然后我使用涉及队列的非阻塞方法来读取标准输出。我发现,尽管该过程每秒向标准输出吐出 1000 行,但标准输出一次阻塞很长时间,完全随机返回一行……千分之一,然后十分之一等等。任何关于为什么会发生这种情况的想法?如果我减慢打印机每 0.5 秒打印一次,我永远不会从标准输出读取数据。就像它需要 1000 次写入 sdtout 才能以单行响应。

注意: 我睡觉了,所以在启动监听线程和阅读它之间有时间。现在,我总是从标准输出返回,但我得到一个随机的单行。我想我不明白标准输出。它不缓冲吗?我的意思是,我想得到我的过程吐出的所有东西,但它似乎只保存了最后一行。我找不到关于这些 PIPE 的文档。

  def getStdOut(self):
        '''
            reads standard out
        '''
        return self.nonBlockingRead()


    def enqueue_output(self, out, queue, kill):
        line = ""
        kill = True
        while kill:
            line = out.readline()
            queue.put(line)

#             for line in iter(out.readline, b''):
#                 queue.put(line)
#             out.close()

    def nonBlockingRead(self):
        '''
             taken from the internet
        '''
        import sys
        from subprocess import PIPE, Popen
        from threading  import Thread
        sleep(0.5) #I inserted this later
        try:
            from Queue import Queue, Empty
        except ImportError:
            from queue import Queue, Empty  # python 3.x

        ON_POSIX = 'posix' in sys.builtin_module_names

        killThread = False                
        self.q = Queue()
        t = Thread(target=self.enqueue_output, args=(self.theProcess.stdout, self.q, killThread))
        t.daemon = True # thread dies with the program
        t.start()

        line = ''
        try:  
            line = self.q.get_nowait() # or q.get(timeout=.1)
        except Exception as e:
            print(e)
            print('no output yet')
        killThread = True
        return line




if __name__ == "__main__" :
    '''
        unit tests
    '''
    import time
    cmd = 'python "C:\InteractiveTest.py"'
    aSubProc = subProcessWStdIn(cmd, 10)
    while True :    
#        print(aSubProc.writeToProcess('y'))
        print(aSubProc.getStdOut())

【问题讨论】:

  • 我已经开始尝试 winpexpect,但令人惊讶的是,这也阻止了!如果你的所有过程都是打印一行然后等待输入,winpexpect 阻塞然后抛出异常!!!

标签: python subprocess stdout nonblocking


【解决方案1】:

readline 从类文件对象PIPE 中读取一行,要全部读取,只需将其包装在一个while循环中即可。您还应该在每次读取后调用 sleep 以节省 CPU 周期。

这是一个简单的例子:

import subprocess

p = subprocess.Popen(
    ['ls', '-lat'],
    shell=False,
    stdout=subprocess.PIPE,
    stderr=subprocess.PIPE,
    stdin=subprocess.PIPE
)
while True:
    line = p.stdout.readline()
    if line == '':
        break
    print(line.strip())  # remove extra ws between lines

编辑:

哇,抱歉,我完全错过了您在其他过程中尝试读取输入的部分......

所以,在你的其他过程中,看起来像:

print('Hello')
in = raw_input()

然后打印实际上将内容发送到您之前传递的类似文件的对象PIPE,它有自己的缓冲机制。 print() function docs

中解释了这种行为

要解决这个问题,只需在 printraw_input 之间添加一个 sys.stdout.flush()

print('Hello')
sys.stdout.flush()  # "flush" the output to our PIPE
in = raw_input()

【讨论】:

  • readline() 被阻塞,因此取决于应用程序。永远不会将该行与空行 ('') 进行比较。这意味着 while 循环将继续运行(永不结束)
  • 我同意,条件是完全任意的,当您的应用程序需要它时,您有责任打破阅读循环
猜你喜欢
  • 1970-01-01
  • 2017-12-30
  • 1970-01-01
  • 2016-07-28
  • 1970-01-01
  • 2011-09-18
  • 2018-06-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多