多处理.池生成的进程比仅在谷歌云上请求的要多



我正在使用Python的多处理。Pool类在进程之间分配任务。

简单的案例如预期:

from multiprocessing import Pool
def evaluate:
do_something()
pool = Pool(processes=N)
for task in tasks:
pool.apply_async(evaluate, (data,))

生成了N个进程,它们不断地完成我传递给apply_async的任务。现在,我有另一个例子,我有许多不同的非常复杂的对象,每个对象都需要进行计算量很大的活动。我最初让每个对象创建自己的多处理。在完成工作时按需池,但我最终遇到了OSError,因为打开了太多文件,尽管我认为池在使用后会被垃圾收集。

无论如何,我决定,无论如何,这些复杂对象中的每一个都最好共享同一个池进行计算:

from multiprocessing import Pool
def evaluate:
do_something()
pool = Pool(processes=N)
class ComplexClass:
def work:
for task in tasks:
self.pool.apply_async(evaluate, (data,))
objects = [ComplexClass() for i in range(50)]
for complex in objects:
complex.pool = pool

while True:
for complex in objects:
complex.work()

现在,当我在我的一台计算机(OSX,Python=3.4)上运行这个程序时,它的工作原理与预期的一样。生成了N个进程,每个复杂对象在每个进程之间分配任务。然而,当我在另一台机器上运行它时(运行Ubuntu的Google Cloud实例,Python=3.5),它产生了大量的进程(>>N),整个程序由于争用而陷入停顿。

如果我查看池以获取更多信息:

import random
random_object = random.sample(objects, 1)
print (random_object.pool.processes)
>>> N

一切看起来都很正常。但事实显然并非如此。有什么想法吗?

更新

我添加了一些额外的日志记录。为了简单起见,我将池大小设置为1。在池中,当任务完成时,我使用os.getpid()打印多处理模块的current_process()以及任务的pid。结果如下:

<ForkProcess(ForkPoolWorker-1, started daemon)>, PID: 5122
<ForkProcess(ForkPoolWorker-1, started daemon)>, PID: 5122
<ForkProcess(ForkPoolWorker-1, started daemon)>, PID: 5122
<ForkProcess(ForkPoolWorker-1, started daemon)>, PID: 5122
...

同样,从使用htop的实际活动来看,我看到许多进程(每个共享多处理池的对象一个)都在消耗CPU周期,这导致了太多的操作系统争用,进度非常慢。5122似乎是父进程。

1。实现了无限循环

如果你实现了一个无限循环,那么它将像无限循环一样运行。你的例子(由于其他原因根本不起作用)。。。

while True:
for complex in objects:
complex.work()

2.繁殖还是分叉过程

尽管上面的代码只显示了一些片段,但在Windows/MacOS和Linux上也不能期望得到相同的结果。前者产生过程,后者分叉。如果您使用可以具有状态的全局变量,则在一个环境上开发和在另一个环境中运行时会遇到问题。

请确保不要在流程中使用全局有状态变量。只需明确地传递它们,或者用另一种方式将其删除。

3.使用程序,而不是脚本

编写一个具有__main__最低要求的程序。特别是,当你使用多处理器时,你需要这个。在该命名空间中实例化您的Pool。

1)您的问题包含与您运行的代码不同的代码(有问题的代码语法不正确,根本无法运行)
2)多处理模块在处理/报告工作人员中发生的错误方面非常糟糕。问题很可能出现在您未显示的代码中。您显示的代码(如果修复)将永远工作并消耗CPU,但它不会在打开太多文件或进程时导致错误。

最新更新