57

複数のプロセス間で共有する必要があるかなり複雑な Python オブジェクトがあります。を使用してこれらのプロセスを起動しmultiprocessing.Processます。オブジェクトを共有するmultiprocessing.Queuemultiprocessing.Pipe、それらは問題なく共有されます。しかし、オブジェクトを他の非マルチプロセッシング モジュール オブジェクトと共有しようとすると、Python がこれらのオブジェクトをフォークしているように見えます。本当?

multiprocessing.Value を使ってみました。しかし、タイプがどうあるべきかわかりませんか?私のオブジェクト クラスは MyClass と呼ばれます。しかし、試してみるとmultiprocess.Value(MyClass, instance)、次のように失敗します。

TypeError: this type has no size

何が起こっているのか分かりますか?

4

6 に答える 6

65

多くの調査とテストを行った結果、「マネージャー」は複雑でないオブジェクト レベルでこのジョブを実行することがわかりました。

以下のコードは、オブジェクトinstがプロセス間で共有されていることを示しています。これは、子プロセスがオブジェクトを変更すると、オブジェクトのプロパティvarinst外部で変更されることを意味します。

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 のパブリッシュ/サブスクライブのような高レベルのツールを複雑なユース ケースに使用できる場合は、常に優れています(私の推奨事項のみ 笑)。

于 2016-09-28T09:07:27.827 に答える
41

これは、Python のmultiprocessing" Manager " クラスとユーザーが定義したプロキシ クラスを使用して行うことができます。Python ドキュメントのプロキシ オブジェクトを参照してください。

やりたいことは、カスタム オブジェクトのプロキシ クラスを定義し、「リモート マネージャー」を使用してオブジェクトを共有することです。ドキュメントが含まれている「リモート マネージャーの使用」セクションで、同じリンクされたドキュメント ページの例を見てください。リモート キューを共有する方法を示します。同じことをしようとしていますが、呼び出しyour_manager_instance.register()の引数リストにカスタム プロキシ クラスが含まれます。

このようにして、カスタム オブジェクトをカスタム プロキシと共有するようにサーバーを設定します。クライアントはサーバーにアクセスする必要があります (ここでも、リモート キューへのクライアント/サーバー アクセスをセットアップする方法の優れたドキュメントの例を参照してください。ただし、 を共有する代わりにQueue、特定のクラスへのアクセスを共有しています)。

于 2010-09-13T00:52:11.133 に答える