Подпроцессы Python испытывают загадочную задержку в получении стандартного EOF - PullRequest
3 голосов
/ 09 февраля 2011

Я сократил проблему, которую видел в своем приложении, до следующего контрольного примера.В этом коде родительский процесс одновременно порождает 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, если это имеет значение.

1 Ответ

6 голосов
/ 19 февраля 2011

Через слишком много времени я понял это, после цитаты из это сообщение выскочило на меня:

См. Раздел «Ввод / вывод на трубах и FIFO» трубы (7) («труба man 7»)

"Если все файловые дескрипторы, относящиеся к концу записи канала, имеют был закрыт, то попытка чтения (2) из ​​канала увидит конец файла (read (2) вернет 0). "

Я должен был знать это, но мне это никогда не приходило в голову - не имело ничего общего с Питоном, в частности. Происходило следующее: подпроцессы разветвлялись с открытыми (записывающими) файловыми дескрипторами в каналы друг друга. Пока в канале есть открытые дескрипторы файла записи, читатели не увидят 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

Оказывается, есть параметр close_fds для Popen, поэтому решение состоит в том, чтобы передать close_fds=True. Все просто и очевидно в ретроспективе, но все же удалось обойтись, по крайней мере, в паре глазных яблок в хорошие моменты времени.

...