web-dev-qa-db-ja.com

Pythonでmultiprocessing.Manager()はどのように機能しますか?

私はPythonのmultiprocessing.Manager()について心配しています、ここに例があります、

import multiprocessing 

def f(ns):

    ns.x *=10
    ns.y *= 10

if __== '__main__':
    manager = multiprocessing.Manager()
    ns = manager.Namespace()
    ns.x = 1
    ns.y = 2

    print 'before', ns
    p = multiprocessing.Process(target=f, args=(ns,))
    p.start()
    p.join()
    print 'after', ns

出力は

before Namespace(x=1, y=2)
after Namespace(x=10, y=20)

今まで、それは私の期待どおりに機能していたので、このようにコードを修正しました、

import multiprocessing 

def f(ns):

    ns.x.append(10)
    ns.y.append(10)

if __== '__main__':
    manager = multiprocessing.Manager()
    ns = manager.Namespace()
    ns.x = []
    ns.y = []

    print 'before', ns
    p = multiprocessing.Process(target=f, args=(ns,))
    p.start()
    p.join()
    print 'after', ns

今、出力は

before Namespace(x=[], y=[])
after Namespace(x=[], y=[])

リストが期待どおりに変更されなかった理由を混乱させましたか?誰が私が何が起こったのかを理解するのを助けることができますか?前もって感謝します!

49
user1231470

マネージャープロキシオブジェクトは、コンテナー内の(管理されていない)可変オブジェクトに加えられた変更を伝達できません。つまり、manager.list()オブジェクトがある場合、管理リスト自体への変更は他のすべてのプロセスに伝播されます。しかし、通常のPython listinsideそのリストがある場合、内部リストへの変更は伝播されません。マネージャーには変更を検出する方法がありません。

変更を伝播するには、ネストされたリストにもmanager.list()オブジェクトを使用する必要があります( Python 3.6以降 が必要)、またはmanager.list()を変更する必要がありますオブジェクト(注 on manager.list in Python 3.5以前 )。

たとえば、次のコードとその出力を検討してください。

import multiprocessing
import time

def f(ns, ls, di):
    ns.x += 1
    ns.y[0] += 1
    ns_z = ns.z
    ns_z[0] += 1
    ns.z = ns_z

    ls[0] += 1
    ls[1][0] += 1 # unmanaged, not assigned back
    ls_2 = ls[2]  # unmanaged...
    ls_2[0] += 1
    ls[2] = ls_2  # ... but assigned back
    ls[3][0] += 1 # managed, direct manipulation

    di[0] += 1
    di[1][0] += 1 # unmanaged, not assigned back
    di_2 = di[2]  # unmanaged...
    di_2[0] += 1
    di[2] = di_2  # ... but assigned back
    di[3][0] += 1 # managed, direct manipulation

if __== '__main__':
    manager = multiprocessing.Manager()
    ns = manager.Namespace()
    ns.x = 1
    ns.y = [1]
    ns.z = [1]
    ls = manager.list([1, [1], [1], manager.list([1])])
    di = manager.dict({0: 1, 1: [1], 2: [1], 3: manager.list([1])})

    print('before', ns, ls, ls[2], di, di[2], sep='\n')
    p = multiprocessing.Process(target=f, args=(ns, ls, di))
    p.start()
    p.join()
    print('after', ns, ls, ls[2], di, di[2], sep='\n')

出力:

before
Namespace(x=1, y=[1], z=[1])
[1, [1], [1], <ListProxy object, typeid 'list' at 0x10b8c4630>]
[1]
{0: 1, 1: [1], 2: [1], 3: <ListProxy object, typeid 'list' at 0x10b8c4978>}
[1]
after
Namespace(x=2, y=[1], z=[2])
[2, [1], [2], <ListProxy object, typeid 'list' at 0x10b8c4630>]
[2]
{0: 2, 1: [1], 2: [2], 3: <ListProxy object, typeid 'list' at 0x10b8c4978>}
[2]

ご覧のとおり、新しい値が管理されたコンテナに直接割り当てられると、値が変更されます。管理対象コンテナ内の可変コンテナに割り当てられた場合、変更されません。ただし、変更可能なコンテナが管理対象コンテナにreassignedである場合、再び変更されます。ネストされた管理されたコンテナの使用も機能し、親コンテナに再度割り当てる必要なく、変更を直接検出します。

51
senderle

nsはNamespaceProxyインスタンスです。これらのオブジェクトには、プロセス間で値を共有できる特別な__getattr____setattr__、および__delattr__メソッドがあります。値を変更するときにこのメカニズムを利用するには、__setattr__をトリガーする必要があります。

ns.x.append(10)

ns.__getattr__を呼び出してns.xを取得しますが、ns.__setattr__は呼び出されません。

これを修正するには、ns.x = ...を使用する必要があります。

def f(ns):   
    tmp = ns.x     # retrieve the shared value
    tmp.append(10)
    ns.x = tmp     # set the shared value
20
unutbu