使用优化的多进程池(multiprocessing.Pool)进行昂贵初始化的优化

12

这是一个完整的简单工作示例

import multiprocessing as mp
import time
import random


class Foo:
    def __init__(self):
        # some expensive set up function in the real code
        self.x = 2
        print('initializing')

    def run(self, y):
        time.sleep(random.random() / 10.)
        return self.x + y


def f(y):
    foo = Foo()
    return foo.run(y)


def main():
    pool = mp.Pool(4)
    for result in pool.map(f, range(10)):
        print(result)
    pool.close()
    pool.join()


if __name__ == '__main__':
    main()

我该如何修改它,以便每个工作进程只初始化一次Foo而不是每个任务都初始化一次?基本上,我希望init被调用4次,而不是10次。我正在使用Python 3.5。


如果只初始化一次类,然后将其复制到每个工作进程中,这样做是否可行? - Brendan Abel
@BrendanAbel 我也这么认为。这意味着对象必须是可pickle的吗?该对象在初始化后永远不会被改变,所以我不知道为什么复制会有问题。 - user2133814
多进程与多线程并不相同,它们具有截然不同的特性。 - jpmc26
抱歉问题标题有些混淆。 - user2133814
2个回答

16

处理类似这样的问题的预期方法是通过可选的initializerinitargs参数来处理Pool()构造函数。它们存在的目的是为了在工作进程创建时给您一种执行某些操作的方式。例如,添加:

def init():
    global foo
    foo = Foo()

并将 Pool 创建更改为:

pool = mp.Pool(4, initializer=init)
如果您需要将参数传递给每个进程的初始化函数,则还需要添加适当的initargs=...参数。
注意:当然,您也应该删除

foo = Foo()

在你的函数中使用由init()创建的全局变量foo,需要从f()返回这个变量。


请问您能解释一下这个上下文中的global关键字吗?我在文档中看到了初始化器,但没有想到/知道"global",所以不知道如何让它工作。谢谢。 - user2133814
刚才我编辑了一下:你还需要“使用”初始化函数创建的 foo。初始化函数只运行一次并结束,所以它所做的任何更改必须在全局范围内可见,以便稍后的其他函数(例如由 map() 调用)可以受益。 - Tim Peters
2
不,没有任何东西在进程之间共享。global自从Python诞生以来就一直存在,比multiprocessing模块的想法早了很多年。global与进程(或线程)无关。在上下文中,它只是告诉init()foo绑定到模块的全局作用域,而不是(默认情况下)init的本地作用域。在多进程中,每个进程都有自己独立的模块全局命名空间。 - Tim Peters

3
最明显的,懒加载。
_foo = None
def f(y):
    global _foo
    if not _foo:
       _foo = Foo()
    return _foo.run(y)

2
为什么你有一个从未被引用的全局变量 _foo - Bakuriu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接