【问题标题】:Persistent python subprocess持久的python子进程
【发布时间】:2012-01-23 23:20:56
【问题描述】:

有没有办法在python“持久”中进行子进程调用?我正在调用一个需要一段时间才能多次加载的程序。因此,如果我可以让该程序保持打开状态并与其通信而不杀死它,那就太好了。

我的python脚本的卡通版是这样的:

for text in textcollection:
    myprocess = subprocess.Popen(["myexecutable"],
                stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                stderr = None)
    myoutputtext, err = myprocess.communicate(input=text)

我需要单独处理每个文本,因此将它们全部加入一个大文本文件并处理一次不是一种选择。

如果有这样的选择最好

myprocess = subprocess.Popen(["myexecutable"],
            stdin = subprocess.PIPE, stdout = subprocess.PIPE,
            stderr = None)    for text in textcollection:
for text in textcollection:
    myoutputtext, err = myprocess.communicate(input=text)

如果我可以让流程保持开放状态,我将不胜感激。

【问题讨论】:

    标签: python subprocess


    【解决方案1】:

    您可以使用myprocess.stdin.write()myprocess.stdout.read() 与您的子进程通信,您只需要注意确保正确处理缓冲以防止您的调用阻塞。

    如果您的子流程的输出定义明确,您应该能够使用行缓冲和myprocess.stdout.readline() 与它可靠地通信。

    这是一个例子:

    >>> p = subprocess.Popen(['cat'], bufsize=1, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    >>> p.stdin.write('hello world\n')
    >>> p.stdout.readline()
    'hello world\n'
    >>> p.stdout.readline()        # THIS CALL WILL BLOCK
    

    对于 Unix,此方法的替代方法是将文件句柄置于非阻塞模式,这将允许您调用像 myprocess.stdout.read() 这样的函数并让它返回数据(如果有),或者引发 IOError 如果没有任何数据:

    >>> p = subprocess.Popen(['cat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    >>> import fcntl, os
    >>> fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
    0
    >>> p.stdout.read()         # raises an exception instead of blocking
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IOError: [Errno 11] Resource temporarily unavailable
    

    这将允许您执行以下操作:

    fcntl.fcntl(p.stdout.fileno(), fcntl.F_SETFL, os.O_NONBLOCK)
    for text in textcollection:
        myprocess.stdin.write(text + '\n')
        while True:
            myoutputtext = ''
            try:
                myoutputtext += myprocess.stdout.read()
            except IOError:
                pass
            if validate_output(myoutputtext):
                break
            time.sleep(.1)    # short sleep before attempting another read
    

    在此示例中,validate_output() 是一个您需要编写的函数,如果您目前收到的数据是您期望获得的所有输出,则返回 True

    【讨论】:

    • 谢谢!我最喜欢您的解决方案,因为它不需要第三方下载。不幸的是,它对我不起作用。在尝试了一些事情之后,我很确定这是我调用的 java 程序的问题,而不是你的解决方案,所以你的解决方案很好。
    • 这是错误的。在编辑任何内容之前,我的支持是无效的,但我看不出有什么可以改进或没有受到伤害。完美的答案。
    • @hynekcer,没问题,感谢您的回复:) 刚刚做了一个小修改。
    • 给我错误`myprocess.stdout.read() 文件“/usr/local/Cellar/python@3.8/3.8.5/Frameworks/Python.framework/Versions/3.8/lib/python3. 8/codecs.py",第 321 行,在 decode data = self.buffer + input TypeError: can't concat NoneType to bytes `
    【解决方案2】:

    是对communicate() 的调用正在杀死您的子进程。根据subprocess documentation communicate() 方法将:

    与进程交互:将数据发送到标准输入。从 stdout 和 stderr 读取数据,直到到达文件结尾。等待进程终止。

    您要做的是直接与POpen 对象的stdinstdout 属性直接交互以与子进程进行通信。但是,文档建议不要这样说:

    警告:使用communicate() 而不是.stdin.write、.stdout.read 或.stderr.read 以避免由于任何其他操作系统管道缓冲区填满并阻塞子进程而导致的死锁。

    因此,您要么需要针对潜在的死锁实施自己的解决方法,要么希望有人为您编写了asynchronous subprocess module

    编辑:这是一个关于如何使用异步子流程模块的简单粗暴的例子:

    import asyncsubprocess
    
    textcollection = ['to', 'be', 'or', 'not', 'to be', 'that is the', 'question']
    
    myprocess = asyncsubprocess.Popen(["cat"],
         stdin = asyncsubprocess.PIPE,
         stdout = asyncsubprocess.PIPE,
         stderr = None)
    
    for text in textcollection:
        bytes_sent, myoutput, err = myprocess.listen(text)
        print text, bytes_sent, myoutput, err
    

    当我运行它时,它会打印:

    to 2 to 
    be 2 be 
    or 2 or 
    not 3 not 
    to be 5 to be 
    that is the 11 that is the 
    question 8 question 
    

    【讨论】:

      【解决方案3】:

      我想你正在寻找

      myprocess.stdin.write(text)
      

      您可以创建一个 Popens 列表,然后在另一个循环中对每个元素调用通信。 像这样的

      processes=[]
      for text in textcollection:
          myprocess = subprocess.Popen(["myexecutable"],
                      stdin = subprocess.PIPE, stdout = subprocess.PIPE,
                      stderr = None)
          myprocess.stdin.write(text)
          processes.append(myprocess)
      
      for proc in processes:
          myoutput, err=proc.communicate()
          #do something with the output here
      

      这样就不必等到所有 Popens 都开始了

      【讨论】:

      • 不幸的是,这对我不起作用,因为它是一个 java 程序,每次运行都会消耗大约 3G 的内存。这就是为什么加载需要这么长时间的原因。我不能拥有 5000 个 3G 进程实例。
      • 我想我明白了。得到输入文本后,它输出一些东西然后退出?还是等你输入别的东西
      【解决方案4】:
      if os.name == 'nt':
       startupinfo = subprocess.STARTUPINFO()
       startupinfo.dwFlags |= subprocess._subprocess.STARTF_USESHOWWINDOW
       subprocess.call(os.popen(tempFileName), shell=True)
       os.remove(tempFileName)
      

      【讨论】:

        猜你喜欢
        • 2010-11-10
        • 2021-03-03
        • 2021-08-13
        • 1970-01-01
        • 2012-08-22
        • 2011-10-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多