繁体   English   中英

带有 importlib 模块的 Python 多进程

[英]Python multiprocess with importlib modules

祝你有个美好的一天
今天我正在将代码从threading转移到multiprocess 一切似乎都很好,直到出现以下错误:

错误

Traceback (most recent call last):
  File "run.py", line 93, in <module>
    main()
  File "run.py", line 82, in main
    emenu.executemenu(components, _path)
  File "/home/s1810979/paellego/lib/execute/execute_menu.py", line 29, in executemenu
    e.executeall(installed, _path)
  File "/home/s1810979/paellego/lib/execute/execute.py", line 153, in executeall
    pool.starmap(phase2, args)
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 268, in starmap
    return self._map_async(func, iterable, starmapstar, chunksize).get()
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 608, in get
    raise self._value
  File "/usr/lib64/python3.4/multiprocessing/pool.py", line 385, in _handle_tasks
    put(task)
  File "/usr/lib64/python3.4/multiprocessing/connection.py", line 206, in send
    self._send_bytes(ForkingPickler.dumps(obj))
  File "/usr/lib64/python3.4/multiprocessing/reduction.py", line 50, in dumps
    cls(buf, protocol).dump(obj)
_pickle.PicklingError: Can't pickle <class 'module'>: attribute lookup module on builtins failed

代码

执行文件

def executeall(components, _path):
    args = []
    manager = multiprocessing.Manager()
    q = manager.Queue()

    resultloc = '/some/result.log'
    for component in components:
        for apkpath, resultpath in zip(execonfig.apkpaths, execonfig.resultpaths):
            args.append((component,apkpath,resultpath,q,)) #Args for subprocesses
    cores = askcores()
    with multiprocessing.Pool(processes=cores) as pool:
        watcher = pool.apply_async(lgr.log, (resultloc+'/results.txt', q,))

        pool.starmap(phase2, args)

组件.py

class Component(object):
    def __init__(self, installmodule, runmodule, installerloc, installationloc, dependencyloc):
        self.installmodule = installmodule
        self.runmodule = runmodule
        self.installerloc = installerloc
        self.installationloc = installationloc
        self.dependencyloc = dependencyloc
        self.config = icnf.Installconfiguration(installerloc+'/conf.conf')
    #lots of functions...

安装配置文件

class State(Enum):
    BEGIN=0 #Look for units
    UNIT=1  #Look for unit keypairs
    KEYPAIR=3

class Phase(Enum):
    NONE=0
    DEPS=1
    PKGS=2

class Installconfiguration(object):
    def __init__(self, config):
        dictionary = self.reader(config) #Fill a dictionary
        #dictionary (key:Phase, value: (dictionary key: str, job))
        self.deps = dictionary[Phase.DEPS]
        self.pkgs = dictionary[Phase.PKGS]

工作.py

class Job(object):
    def __init__(self, directory=None, url=None):
        self.directory = directory if directory else ''
        self.url = url if url else ''

如您所见,我将一个组件作为参数传递给function phase2(component, str, str, multiprocess.manager.Queue())

component构造函数的第二个和第三个参数是用importlib导入的模块。


我试过的

我是 Python 新手,但不是编程新手。 这是我尝试过的:

  • 因为错误本身并没有指出问题究竟是什么,我尝试删除 args 以找出哪些不能被腌制:删除component ,一切正常,所以这似乎是问题的原因。 但是,我需要将此对象传递给我的进程。
  • 我在互联网上搜索了几个小时,但除了关于多处理的基本教程和关于泡菜如何工作的解释之外,什么也没找到。 我确实发现说它应该可以工作,但不能在 Windows 或其他东西上使用。 但是,它不适用于 Unix(我使用的)

我的想法

据我了解,没有任何迹象表明我不能发送包含两个 importlib 模块的类。 我不知道component类的确切问题是什么,但作为成员的 importlib 模块是唯一的非常规事物。 这就是为什么我认为问题发生在这里。


您知道为什么包含模块的类不适合“酸洗”吗? 如何更好地了解Can't pickle <class 'module'>错误发生的原因和位置?

更多代码

可以在https://github.com/Sebastiaan-Alvarez-Rodriguez/paellego上找到完整的源代码

向我提问

请留下评论要求澄清/更多代码片段/??? 如果你想让我编辑这个问题

最后一个请求

我希望解决方案只使用 python 标准库,最好使用 python 3.3。 另外,我的代码要求它在 Unix 系统上运行。

提前致谢


编辑

根据要求,这是一个大大简化问题的最小示例:
main.py(你可以作为 python main.py foo 执行)

#!/usr/bin/env python
import sys
import importlib
import multiprocessing
class clazz(object):
    def __init__(self, moduly):
        self.moduly = moduly

    def foopass(self, stringy):
        self.moduly.foo(stringy)

    def barpass(self, stringy, numbery):
        self.moduly.bar(stringy)
        print('Second argument: '+str(numbery))

def worker(clazzy, numbery):
    clazzy.barpass('wow', numbery)

def main():
    clazzy = clazz(importlib.import_module(sys.argv[1]))
    clazzy.foopass('init')

    args = [(clazzy, 2,)]
    with multiprocessing.Pool(processes=2) as pool:
        pool.starmap(worker, args)

if __name__ == "__main__":
    main()

foo.py(需要在同一目录中才能获得上述调用建议):

#!/usr/bin/env python
globaly = 0

def foo(stringy):
    print('foo '+stringy)
    global globaly
    globaly = 5

def bar(stringy):
    print('bar '+stringy)
    print(str(globaly))

这会在运行时出错: TypeError: can't pickle module objects现在我们知道(遗憾的是)不可能腌制模块对象。

为了摆脱这个错误,让clazz不把一个模块作为属性,无论多么方便,而是让它带“ modpath ”,这是importlib导入用户指定的模块所需的字符串。
它看起来像这样(foo.py 与上面的完全相同):

#!/usr/bin/env python
import sys
import importlib
import multiprocessing
class clazz(object):
    def __init__(self, modpathy):
        self.modpathy = modpathy

    def foopass(self, stringy):
        moduly = importlib.import_module(self.modpathy)
        moduly.foo(stringy)

    def barpass(self, stringy, numbery):
        moduly = importlib.import_module(self.modpathy)
        moduly.bar(stringy)
        print('Second argument: '+str(numbery))

def worker(clazzy, number):
    clazzy.barpass('wow', number)

def main():
    clazzy = clazz(sys.argv[1])
    clazzy.foopass('init')

    args = [(clazzy, 2,)]
    with multiprocessing.Pool(processes=2) as pool:
        pool.starmap(worker, args)

if __name__ == "__main__":
    main()

如果您要求您的全局变量(例如globaly )保证保持状态,那么您需要传递一个可变对象(例如列表、字典)来保存这些数据,感谢@DavisHerring:

模块属性在 Python 中被称为“全局变量”,但它们并不比任何其他数据更持久或可访问。 为什么不直接使用字典?

示例代码如下所示:

#!/usr/bin/env python
import sys
import importlib
import multiprocessing
class clazz(object):
    def __init__(self, modpathy):
        self.modpathy = modpathy
        self.dictionary = {}

    def foopass(self, stringy):
        moduly = importlib.import_module(self.modpathy)
        moduly.foo(stringy, self.dictionary)

    def barpass(self, stringy, numbery):
        moduly = importlib.import_module(self.modpathy)
        moduly.bar(stringy, self.dictionary)
        print('Second argument: '+str(numbery))

def worker(clazzy, number):
    clazzy.barpass('wow', number)

def main():
    clazzy = clazz(sys.argv[1])
    clazzy.foopass('init')

    args = [(clazzy, 2,)]
    with multiprocessing.Pool(processes=2) as pool:
        pool.starmap(worker, args)

if __name__ == "__main__":
    main()

foo.py(不再有全局变量):

#!/usr/bin/env python

def foo(stringy, dictionary):
    print('foo '+stringy)
    globaly = 5
    dictionary['globaly'] = globaly

def bar(stringy, dictionary):
    print('bar '+stringy)
    globaly = dictionary['globaly']
    print(str(globaly))

通过这种方式,您可以解决问题而不会烦人的can't pickle ...错误,同时保持状态

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM