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

Python 在进程之间共享复杂对象?,python,process,multiprocessing,sharing,Python,Process,Multiprocessing,Sharing,我有一个相当复杂的Python对象,需要在多个进程之间共享。我使用多处理.Process启动这些进程。当我与其中的multiprocessing.Queue和multiprocessing.Pipe共享一个对象时,它们可以很好地共享。但是,当我尝试与其他非多处理模块对象共享一个对象时,Python似乎将这些对象分叉。这是真的吗 我尝试使用multiprocessing.Value。但我不确定应该是什么类型的?我的对象类称为MyClass。但是当我尝试multiprocess.Value(MyCl

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

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

TypeError:此类型没有大小


知道发生了什么吗?

您可以使用Python的
多处理类和您定义的代理类来实现这一点。请参见Python文档中的

您要做的是为您的自定义对象定义一个代理类,然后使用“远程管理器”共享该对象——请查看“”部分中相同链接文档页面中的示例,其中文档显示了如何共享远程队列。您将执行相同的操作,但是对
您的\u manager\u instance.register()
的调用将在其参数列表中包含自定义代理类


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

经过大量研究和测试,我找到了“Manager”在非复杂对象级别执行此项工作

下面的代码显示对象
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
来自多处理导入进程的管理器 从multiprocessing.managers导入BaseManager 类SimpleClass(对象): 定义初始化(自): self.var=0 def设置(自身,值): self.var=值 def get(自我): 返回self.var def更改obj值(obj): 对象集(100) 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': BaseManager.register('SimpleClass',SimpleClass) manager=BaseManager() manager.start() inst=manager.SimpleClass() p=过程(目标=更改对象值,参数=[inst]) p、 开始() p、 加入 印刷仪器 打印安装获取()#100
好的,如果您只需要共享简单对象,上面的代码就足够了

为什么不复杂?因为如果对象嵌套(对象内部的对象),它可能会失败

来自多处理导入进程的管理器 从multiprocessing.managers导入BaseManager 类GetSetter(对象): 定义初始化(自): self.var=None def设置(自身,值): self.var=值 def get(自我): 返回self.var 类ChildClass(GetSetter): 通过 类父类(GetSetter): 定义初始化(自): self.child=ChildClass() GetSetter.\uuuu init\uuuuu(self) def getChild(self): 返回自己的孩子 def更改obj值(obj): 对象集(100) obj.getChild().set(100) 如果uuuu name uuuuuu='\uuuuuuu main\uuuuuuu': BaseManager.register('ParentClass',ParentClass) manager=BaseManager() manager.start() inst2=manager.ParentClass() p2=过程(目标=更改对象值,参数=[inst2]) p2.start() p2.join() 打印inst2# 打印inst2.getChild() 打印inst2.get()#100 #好!! 打印inst2.getChild().get()#无 #糟糕!你们也需要注册子类,但几乎并没有办法 #即使您注册了子类,也可能会得到PicklingError:)
我认为这种行为的主要原因是
Manager
只是在管道/队列等低级通信工具之上构建的candybar


因此,这种方法在多处理情况下不被很好地推荐。对于复杂的用例,如果您可以使用低级工具(如lock/semaphore/pipe/queue)或高级工具(如Redis queueRedis publish/subscribe),效果会更好(仅限我的推荐lol)

for result in in pool.imap_unordered(process_function, iterable_data):
    do_something(result)

如果返回的数据不多,那么执行此操作可能不会有太多开销。

这里有一个我专门为此制作的python包(在进程之间共享复杂对象)

吉特:

其思想是为对象创建代理并将其传递给进程。然后使用代理,就像使用原始对象的引用一样。虽然您只能使用方法调用,但访问对象变量是通过setter和getter完成的

假设我们有一个名为“示例”的对象,创建代理和代理侦听器很容易:

from pipeproxy import proxy 
example = Example() 
exampleProxy, exampleProxyListener = proxy.createProxy(example) 
现在将代理发送到另一个进程

p = Process(target=someMethod, args=(exampleProxy,)) p.start()
在其他过程中使用它,就像使用原始对象一样(示例):

但你必须在主要的过程中聆听它:

exampleProxyListener.listen()
阅读更多内容并在此处找到示例:


我尝试使用BaseManager并注册我的定制类,以使其满意,并解决了嵌套类的问题,正如Tom前面提到的那样

我认为主要原因与前面提到的嵌套类无关,但是python采用的通信机制是低级的。原因是python使用了一些类似套接字的通信机制来同步服务器进程中定制类的修改。我瘦了
def someMethod(exampleProxy):
    ...
    exampleProxy.originalExampleMethod()
    ...
exampleProxyListener.listen()
from multiprocessing import Process, Manager, Lock
from multiprocessing.managers import BaseManager
import numpy as np

class NestedObj(object):
       def __init__(self):
                self.val = 1

class CustomObj(object):
        def __init__(self, numpy_obj):
                self.numpy_obj = numpy_obj
                self.nested_obj = NestedObj()

        def set_value(self, p, q, v):
                self.numpy_obj[p, q] = v

        def get_obj(self):
                return self.numpy_obj

        def get_nested_obj(self):
                return self.nested_obj.val

class CustomProcess(Process):
        def __init__(self, obj, p, q, v):
                super(CustomProcess, self).__init__()
                self.obj = obj
                self.index = p, q
                self.v = v

        def run(self):
                self.obj.set_value(*self.index, self.v)



if __name__=="__main__":
        BaseManager.register('CustomObj', CustomObj)
        manager = BaseManager()
        manager.start()
        data = [[0 for x in range(10)] for y in range(10)]
        matrix = np.matrix(data)
        custom_obj = manager.CustomObj(matrix)
        print(custom_obj.get_obj())
        process_list = []
        for p in range(10):
                for q in range(10):
                        proc = CustomProcess(custom_obj, p, q, 10*p+q)
                        process_list.append(proc)
        for x in range(100):
                process_list[x].start()
        for x in range(100):
                process_list[x].join()
        print(custom_obj.get_obj())
        print(custom_obj.get_nested_obj())