在Python进程之间共享一个复杂的对象?

我有一个相当复杂的Python对象,我需要在多个进程之间共享。 我使用multiprocessing.Process启动这些进程。 当我使用multiprocessing.Queuemultiprocessing.Pipe共享一个对象时,它们可以很好地共享。 但是,当我尝试与其他非多处理模块对象共享一个对象时,似乎Python会分叉这些对象。 真的吗?

我尝试使用multiprocessing.Value。 但我不确定这种类型应该是什么? 我的对象类叫做MyClass。 但是,当我尝试multiprocess.Value(MyClass, instance) ,它失败:

TypeError: this type has no size

任何想法发生了什么?


您可以使用Python的Multiprocessing“Manager”类和您定义的代理类来执行此操作。 从Python文档:http://docs.python.org/library/multiprocessing.html#proxy-objects

你想要做的是为你的自定义对象定义一个代理类,然后使用“远程管理器”共享对象 - 查看文档显示如何共享的“远程管理器”的相同链接文档页面中的示例一个远程队列。 你将会做同样的事情,但是你对your_manager_instance.register()的调用将会在你的自变量列表中包含你的自定义代理类。

通过这种方式,您可以设置一个服务器来与自定义代理共享自定义对象。 您的客户需要访问服务器(同样,请参阅有关如何设置客户端/服务器访问远程队列的优秀文档示例,但不共享队列,您将共享访问您的特定类)。


经过大量的研究和测试后,我发现“经理”在非复杂的对象层面上完成这项工作。

下面的代码显示了对象inst在进程之间共享,这意味着当进程改变它时, inst属性var在外面被改变。

from multiprocessing import Process, Manager
from multiprocessing.managers import BaseManager

class SimpleClass(object):
    def __init__(self):
        self.var = 0

    def set(self, value):
        self.var = value

    def get(self):
        return self.var


def change_obj_value(obj):
    obj.set(100)


if __name__ == '__main__':
    BaseManager.register('SimpleClass', SimpleClass)
    manager = BaseManager()
    manager.start()
    inst = manager.SimpleClass()

    p = Process(target=change_obj_value, args=[inst])
    p.start()
    p.join()

    print inst                    # <__main__.SimpleClass object at 0x10cf82350>
    print inst.get()              # 100

好的,如果你只需要分享简单的对象 ,上面的代码就足够了

为什么没有复杂? 因为如果你的对象是嵌套的(对象在对象内), 它可能会失败

from multiprocessing import Process, Manager
from multiprocessing.managers import BaseManager

class GetSetter(object):
    def __init__(self):
        self.var = None

    def set(self, value):
        self.var = value

    def get(self):
        return self.var


class ChildClass(GetSetter):
    pass

class ParentClass(GetSetter):
    def __init__(self):
        self.child = ChildClass()
        GetSetter.__init__(self)

    def getChild(self):
        return self.child


def change_obj_value(obj):
    obj.set(100)
    obj.getChild().set(100)


if __name__ == '__main__':
    BaseManager.register('ParentClass', ParentClass)
    manager = BaseManager()
    manager.start()
    inst2 = manager.ParentClass()

    p2 = Process(target=change_obj_value, args=[inst2])
    p2.start()
    p2.join()

    print inst2                    # <__main__.ParentClass object at 0x10cf82350>
    print inst2.getChild()         # <__main__.ChildClass object at 0x10cf6dc50>
    print inst2.get()              # 100
    #good!

    print inst2.getChild().get()   # None
    #bad! you need to register child class too but there's almost no way to do it
    #even if you did register child class, you may get PicklingError :)

我认为这种行为的主要原因是因为Manager只是在管道/队列等低级通信工具的基础上构建的直板。

所以,这种方法适合用于多处理案例。 如果你可以使用像lock / semaphore / pipe / queue这样的低级工具或Redis 队列Redis发布/订阅复杂用例这样的高级工具(只有我的推荐lol),那会更好。


这里是我为此所做的一个python包(在进程之间共享复杂的对象)。

git:https://github.com/dRoje/pipe-proxy

这个想法是你为你的对象创建一个代理并将它传递给一个进程。 然后你使用代理就像你有一个对原始对象的引用。 尽管你只能使用方法调用,所以访问对象变量时会抛出setter和getters。

假设我们有一个名为'example'的对象,创建代理和代理监听器非常简单:

from pipeproxy import proxy 
example = Example() 
exampleProxy, exampleProxyListener = proxy.createProxy(example) 

现在您将代理发送到另一个进程。

p = Process(target=someMethod, args=(exampleProxy,)) p.start()

像使用原始对象一样在其他进程中使用它(示例):

def someMethod(exampleProxy):
    ...
    exampleProxy.originalExampleMethod()
    ...

但是你必须在主要过程中听取它:

exampleProxyListener.listen()

阅读更多内容并在这里找到示例

http://matkodjipalo.com/index.php/2017/11/12/proxy-solution-python-multiprocessing/

链接地址: http://www.djcxy.com/p/46467.html

上一篇: Sharing a complex object between Python processes?

下一篇: NodeJS domain error handling not picking up ReferenceError