【问题标题】:Python subprocesses experience mysterious delay in receiving stdin EOFPython 子进程在接收标准输入 EOF 时遇到神秘延迟
【发布时间】:2011-06-23 20:38:24
【问题描述】:

我将我在应用程序中看到的问题简化为以下测试用例。在这段代码中,一个父进程同时生成 2 个(您可以生成更多)子进程,它们通过标准输入从父进程读取一条大消息,休眠 5 秒,然后写回一些内容。但是,某处发生了意外等待,导致代码在 10 秒内完成,而不是预期的 5 秒。

如果您设置verbose=True,您可以看到分散的子进程正在接收大部分消息,然后等待最后一块 3 个字符——它没有检测到管道已关闭。此外,如果我对第二个进程 (doreturn=True) 根本不做任何事情,第一个进程将永远看到 EOF。

有什么想法吗?再往下是一些示例输出。提前致谢。

from subprocess import *
from threading import *
from time import *
from traceback import *
import sys
verbose = False
doreturn = False
msg = (20*4096+3)*'a'
def elapsed(): return '%7.3f' % (time() - start)
if sys.argv[1:]:
  start = float(sys.argv[2])
  if verbose:
    for chunk in iter(lambda: sys.stdin.read(4096), ''):
      print >> sys.stderr, '..', time(), sys.argv[1], 'read', len(chunk)
  else:
    sys.stdin.read()
  print >> sys.stderr, elapsed(), '..', sys.argv[1], 'done reading'
  sleep(5)
  print msg
else:
  start = time()
  def go(i):
    print elapsed(), i, 'starting'
    p = Popen(['python','stuckproc.py',str(i), str(start)], stdin=PIPE, stdout=PIPE)
    if doreturn and i == 1: return
    print elapsed(), i, 'writing'
    p.stdin.write(msg)
    print elapsed(), i, 'closing'
    p.stdin.close()
    print elapsed(), i, 'reading'
    p.stdout.read()
    print elapsed(), i, 'done'
  ts = [Thread(target=go, args=(i,)) for i in xrange(2)]
  for t in ts: t.start()
  for t in ts: t.join()

示例输出:

  0.001 0 starting
  0.003 1 starting
  0.005 0 writing
  0.016 1 writing
  0.093 0 closing
  0.093 0 reading
  0.094 1 closing
  0.094 1 reading
  0.098 .. 1 done reading
  5.103 1 done
  5.108 .. 0 done reading
 10.113 0 done

如果有区别的话,我正在使用 Python 2.6.5。

【问题讨论】:

    标签: python pipe subprocess


    【解决方案1】:

    经过太多时间,我想通了,this post 的一句话突然出现在我面前:

    参见 pipe(7) 的“I/O on Pipes and FIFOs”部分(“man 7 pipe”)

    "如果所有引用管道写端的文件描述符都有 已关闭,然后尝试从管道读取(2)将看到 文件结尾(read(2) 将返回 0)。”

    我应该知道这一点,但我从来没有想过——特别是与 Python 无关。发生的事情是:子进程被打开(写入器)文件描述符分叉到彼此的管道。只要管道中有打开的写入器文件描述符,读取器就不会看到 EOF。

    例如:

    p1=Popen(..., stdin=PIPE, ...) # creates a pipe the parent process can write to
    p2=Popen(...) # inherits the writer FD - as long as p2 exists, p1 won't see EOF
    

    原来Popen 有一个close_fds 参数,所以解决方案是传递close_fds=True。事后看来,一切都简单明了,但仍然设法花费了至少几个眼球的大量时间。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-29
      • 2011-09-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多