如何使用 multiprocessing.Manager()?
- 2025-03-20 08:48:00
- admin 原创
- 36
问题描述:
我对 Python 有一个担忧multiprocessing.Manager()
。以下是示例:
import multiprocessing
def f(ns):
ns.x *=10
ns.y *= 10
if __name__ == '__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 __name__ == '__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=[])
我很困惑为什么列表没有像我预期的那样更改。有人能帮我弄清楚发生了什么吗?
解决方案 1:
管理器代理对象无法传播对容器内(非托管)可变对象所做的更改。换句话说,如果您有一个对象,则对托管列表本身的任何更改都会传播到所有其他进程。但是,如果您在该列表中manager.list()
有一个普通的 Python 列表,则不会传播对内部列表的任何更改,因为管理器无法检测到更改。
为了传播更改,您manager.list()
也必须对嵌套列表使用对象(需要Python 3.6 或更新版本),或者您需要直接修改对象(请参阅Python 3.5 或更早版本中的注释manager.list()
)。manager.list
例如,考虑以下代码及其输出:
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 __name__ == '__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='
')
p = multiprocessing.Process(target=f, args=(ns, ls, di))
p.start()
p.join()
print('after', ns, ls, ls[2], di, di[2], sep='
')
输出:
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]
如您所见,当新值直接分配给托管容器时,它会发生变化;当将其分配给托管容器内的可变容器时,它不会发生变化;但如果将可变容器重新分配给托管容器,它会再次发生变化。使用嵌套托管容器也可以,直接检测更改,而不必分配回父容器。
解决方案 2:
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
相关推荐
热门文章
项目管理软件有哪些?
热门标签
曾咪二维码
扫码咨询,免费领取项目管理大礼包!
云禅道AD