2011-02-09 78 views
3

我減少了一個問題,我在我的應用程序中看到下面的測試用例。在這段代碼中,一個父進程同時產生2個子進程(你可以產生更多的子進程),通過標準輸入從父進程讀取一個大消息,休眠5秒,然後寫回一些東西。但是,有意外的等待發生在某處,導致代碼在10秒內完成,而不是預期的5.Python子進程在接收標準輸入EOF時遇到神祕延遲

如果設置了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如果有差別。

回答

6

經過了太多的時間,我想通了,報價之後,從this post我跳了出來:

請參閱「關於管道和FIFO I/O」管段(7)(」 man 7 pipe「)

」如果所有涉及管道寫入結束的文件描述符都已關閉,則嘗試從管道讀取(2)將看到 文件結束(讀取(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 

原來有一個close_fds參數Popen,因此該解決方案是通過close_fds=True。事後看來,這一切都很簡單明顯,但仍然至少花費了幾個小時的時間。

+0

很高興你發現了 - 絕對是我在閱讀(3行)參數文檔時錯過的東西。 – phooji 2011-02-20 23:42:51