为什么在与多个Popen子进程一起使用时会传递死锁?

时间:2013-01-30 22:57:33

标签: python python-2.7 multiprocessing subprocess

以下问题在Python 2.7.3中发生 not 。但是,它在我的机器上运行Python 2.7.1和Python 2.6(64位Mac OSX 10.7.3)。这是我最终将分发的代码,所以我想知道是否有任何方法可以完成此任务,而不是如此显着地依赖于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))时,进程将无限期地阻塞。我已经尝试过手动写入stdin的替代方法:

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

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

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

1 个答案:

答案 0 :(得分:9)

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

此处描述了问题(以及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()/ communic()他们创建的顺序相反,似乎有效。

即代替:

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()'。)