使用带有修饰函数的多处理会导致PicklingError

时间:2014-10-26 18:40:50

标签: python multiprocessing

我正在尝试编写一个基于multiprocessing库的便捷函数,它接受任何函数和参数,并使用多个进程运行该函数。我有以下文件“MultiProcFunctions.py”我正在导入:

import multiprocessing
from multiprocessing import Manager

def MultiProcDecorator(f,*args):

    """
    Takes a function f, and formats it so that results are saved to a shared dict
    """

    def g(procnum,return_dict,*args):
        result = f(*args)
        return_dict[procnum] = result

    return g

def MultiProcFunction(f,n_procs,*args):
    """
    Takes a function f, and runs it in n_procs with given args
    """

    manager     = Manager()
    return_dict = manager.dict()

    jobs = []
    for i in range(n_procs):
        p = multiprocessing.Process( target = f, args = (i,return_dict) + args )
        jobs.append(p)
        p.start()

    for proc in jobs:
        proc.join()

    return dict(return_dict)

这是我运行的代码:

from MultiProcFunctions import *

def sq(x):
    return [i**2 for i in x]

g = MultiProcDecorator(sq)

if __name__ == '__main__':

    result = MultiProcFunction(g,2,[1,2,3])

我收到以下错误:PicklingError: Can't pickle <function g at 0x01BD83B0>: it's not found as MultiProcFunctions.g

如果我使用g的以下定义,那么一切都很好:

def g(procnum,return_dict,x):
    result = [i**2 for i in x]
    return_dict[procnum] = result

为什么g的两个定义不同,有什么办法可以让原始g定义“工作”?

2 个答案:

答案 0 :(得分:2)

这种情况正在发生,因为g实际上被定义为MultiProcFunctions中的嵌套函数,这意味着它实际上不能从该模块的顶层导入,这意味着它赢了&# 39;酸洗得恰到好处。现在,我们在g模块的顶层实际上非常清楚地定义了__main__,但是当我们这样做时:

g = MultiProcDecorator(sq)

所以,应该可以选择。我们可以通过明确将__module__ g "__main__"设为g = MultiProcDecorator(sq) g.__module__ = "__main__" # Fix the __module__ 来使其发挥作用:

g

这将允许酸洗过程正常工作,因为它将在__main__中查找MultiProcFunctions的定义,其中定义在顶级,而不是def MultiProcDecorator(f,*args): """ Takes a function f, and formats it so that results are saved to a shared dict """ def g(procnum,return_dict,*args): result = f(*args) return_dict[procnum] = result g.__module__ = "__main__" return g ,它只在嵌套范围内定义。

修改

请注意,您也可以在装饰器本身进行更改:

multiprocessing

这可能对你更有意义,因为这个装饰器严格意味着用于{{1}}目的。

答案 1 :(得分:1)

尝试dano's trick似乎只适用于Python 2.在Python 3中尝试时,我收到以下错误:

  

pickle.PicklingError:不能挑选<function serialize at 0x7f7a1ac1fd08>:它与 main 不同的对象.orig_fn

我通过&#34;装饰&#34;解决了这个问题。来自worker的初始函数:

import sys

def worker_init(fn, *args):
    @wraps(fn)
    def wrapper(x):
        # wrapper logic
        pass

    setattr(sys.modules[fn.__module__], fn.__name__, wrapper)

pool = mp.Pool(initializer=worker_init, initargs=[orig_fn, *args])
# ...