为啥与多个 Popen 子进程一起使用时会出现通信死锁?

Posted

技术标签:

【中文标题】为啥与多个 Popen 子进程一起使用时会出现通信死锁?【英文标题】:Why does communicate deadlock when used with multiple Popen subprocesses?为什么与多个 Popen 子进程一起使用时会出现通信死锁? 【发布时间】:2013-01-14 22:41:04 【问题描述】:

在 Python 2.7.3 中不会出现以下问题。但是,我的机器(64 位 Mac OSX 10.7.3)上的 Python 2.7.1 和 Python 2.6 都会出现这种情况。这是我最终将分发的代码,所以我想知道是否有任何方法可以完成这项任务,而不是过于依赖 Python 版本。

我需要并行打开多个子进程并将 STDIN 数据写入每个子进程。通常我会使用Popen.communicate 方法来做到这一点。但是,每当我同时打开多个进程时,communicate 就会死锁。

import subprocess

cmd = ["grep", "hello"]
processes = [subprocess.Popen(cmd, stdin=subprocess.PIPE,
                              stdout=subprocess.PIPE, stderr=subprocess.PIPE)
                                for _ in range(2)]

for p in processes:
    print p.communicate("hello world\ngoodbye world\n")

如果我把进程数改成for _ in range(1),输出和预期一样:

('hello world\n', '')

但是,当有两个进程 (for _ in range(2)) 时,进程会无限期地阻塞。我已经尝试过手动写入标准输入的替代方法:

for p in processes:
    p.stdin.write("hello world\ngoodbye world\n")

但是任何从进程中读取的尝试(例如p.stdout.read())仍然会死锁。

起初this 似乎是相关的,但它指定它在使用多个线程时发生,并且死锁很少发生(而这里总是发生)。有没有办法让它在 2.7.3 之前的 Python 版本上工作?

【问题讨论】:

【参考方案1】:

我不得不为此挖掘一点。 (我曾经遇到过类似的问题,所以以为我知道答案,但是错了。)

此处描述了该问题(以及 2.7.3 的补丁):

http://bugs.python.org/issue12786

问题在于 PIPE 被子进程继承。答案是在您的 Popen 调用中使用 'close_fds=True'。

processes = [subprocess.Popen(cmd, stdin=subprocess.PIPE,
               stdout=subprocess.PIPE, stderr=subprocess.PIPE,close_fds=True)
                            for _ in range(2)]

如果这会导致您想要重复使用的其他文件描述符出现问题(如果这是一个简化的示例),那么您可以使用 wait()/communicate() 与它们的创建顺序相反,它似乎可以工作。

即,而不是:

for p in processes:
    print p.communicate("hello world\ngoodbye world\n")

使用:

while processes:
    print processes.pop().communicate("hello world\ngoodbye world\n")

(或者,我猜,只需在现有循环之前执行 'processes.reverse()'。)

【讨论】:

windows下close_fds的解决方案似乎有问题:ValueError: close_fds is not supported on Windows platforms if you redirect stdin/stdout/stderr

以上是关于为啥与多个 Popen 子进程一起使用时会出现通信死锁?的主要内容,如果未能解决你的问题,请参考以下文章

在Python中,如何将多个进程与popen链接在一起,同时将中间部分结果重定向到一个字符串中

python子进程中的多个输入和输出通信

如何使用 python 通过子进程与 Excel 文件进行通信? [关闭]

如何加快与子进程的通信

可以python子进程Popen接受多个stdin流吗?

C - SIGINT处理程序不能与多线程一起工作,每个线程都有一个popen进程。