在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))时,该进程将无限期阻塞。我尝试过手动编写stdin的替代方法:
for p in processes:
    p.stdin.write("hello world\ngoodbye world\n")

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

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

最佳答案

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

问题(和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()”即可。)

关于python - 与多个Popen子进程一起使用时,为什么会出现死锁?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/14615462/

10-12 16:08
查看更多