为什么`popen.stdout.readline`会死锁,并且该怎么办?

问题描述 投票:0回答:1

Python documentation开始

警告使用communicate()而不是.stdin.write.stdout.read.stderr.read,以避免死锁是由于其他OS管道缓冲区中的任何一个填满并阻塞了子进程。

我试图理解为什么这会造成僵局。对于某些背景,我并行生成N个进程:

for c in commands:
    h = subprocess.Popen(c, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    handles.append(h)

然后逐个打印每个进程的输出:

for handle in handles:
    while handle.poll() is None:
        try:
            line = handle.stdout.readline()
        except UnicodeDecodeError:
            line = "((INVALID UNICODE))\n"

        sys.stdout.write(line)
    if handle.returncode != 0:
        print(handle.stdout.read(), file=sys.stdout)
    if handle.returncode != 0:
        print(handle.stderr.read(), file=sys.stderr)

有时实际上确实会造成僵局。不幸的是,文档中建议使用communicate()的建议对我来说不起作用,因为此过程可能要花几分钟的时间才能运行,而且我不希望它在这段时间内显得死气沉沉。它应该实时打印输出。

我有几种选择,例如更改bufsize参数,在每个句柄中轮询不同的线程等。但是,为了确定解决此问题的最佳方法,我想我需要了解根本原因因为僵局在首位。显然,这与缓冲区大小有关,但是又如何呢?我可以假设所有这些进程都共享一个OS内核对象,并且由于我只消耗了其中一个进程的缓冲区,其他进程将其填满,在这种情况下,上述选项2可能会解决该问题。但这也许不是真正的问题。

任何人都可以阐明这一点吗?

python python-3.x subprocess
1个回答
1
投票

父进程和子进程之间的双向通信使用两个单向管道。每个方向一个。好的,stderr是第三个,但是想法是相同的。

一个管道有两个末端,一个末端用于书写,一个末端用于读取。管道的容量为4K,在现代Linux上现在为64K。可以期望在其他系统上具有相似的值。这意味着,编写器可以毫无问题地写入管道,但管道不会满,然后管道将写满并且阻塞写入,直到读取器从另一端读取一些数据为止。

© www.soinside.com 2019 - 2024. All rights reserved.