是否复制了多处理管理器注册类型的参数?



我正在尝试转换我的类,以便其他进程可以访问它。

import multiprocessing as mp
from multiprocessing.managers import BaseManager
import time

class SharedType:
def __init__(self, custom_arg):
self.count = 0
self.custom_arg = custom_arg
self.builtin_arg = []
def accum(self):
self.count += 1
def get_count(self):
return self.count
def get_custom_arg(self):
return self.custom_arg
def get_builtin_arg(self):
return self.builtin_arg

class CustomArg:
def __init__(self):
self.v = []
def append(self, i):
self.v.append(i)

class MyManager(BaseManager):
pass

MyManager.register('SharedType', SharedType)

def count(obj, uid):
print(uid, 'start')
# record uid
obj.get_custom_arg().append(uid)
obj.get_builtin_arg().append(uid)
# do some work
for _ in range(100_000):
obj.accum()
return id

if __name__ == '__main__':
print('run single process')
st = time.time()
c = 0
for _ in range(10):
for __ in range(100_000):
c += 1
print(c, f'elapse: {time.time() - st}')
print()
print('run multi process')
st = time.time()
pool = mp.Pool()
with MyManager() as manager:
shared_obj = manager.SharedType(CustomArg())
# run
ps = [pool.apply_async(count, args=(shared_obj, i)) for i in range(10)]
print('waiting...')
pool.close()
pool.join()
print(shared_obj.get_count(), f'elapse: {time.time() - st}')
print(shared_obj.get_custom_arg().v, shared_obj.get_builtin_arg())

结果如下:

run single process
1000000 elapse: 0.14413094520568848
run multi process
waiting...
0 start
1 start
2 start
4 start
5 start
3 start
7 start
8 start
6 start
9 start
1000000 elapse: 36.199955463409424
[] []

我期望看到shared_obj的属性像shared_obj.count一样保持共享,而不是被复制到进程自己的记忆中。那么有没有办法共享包括复杂属性在内的整个属性呢?还是这样的想法完全是胡说八道?

此外,在上述情况下,使多处理如此缓慢的开销是什么?

尽管multiprocessing有时试图假装,但在 Python 中,你根本无法让一个对象同时存在于两个进程中。


在代码中,管理器与服务器进程相关联,SharedType实例位于该服务器进程中。主节点的shared_obj是一个代理对象,它与服务器进程通信以进行方法调用。当您将shared_obj传递给pool.apply_async时,工人也会获得代理,这些代理是通过腌制主代理并解脱工作线程中的泡菜创建的。

在此类代理上调用方法时,所有参数都将被提炼,被提法的表示形式将通过进程间通信发送到服务器,服务器会解取它们以构造新对象。然后,服务器在SharedType实例上调用该方法,将酸洗返回值,并将酸洗数据发送回请求方法调用的进程,该进程取消酸洗以构造自己的方法返回值副本。

(所有这些酸洗和脱腌以及进程间通信真的很慢,这就是为什么多处理会减慢你的代码。


count

实际上并不比此处的其他属性更共享。不同之处在于,您通过代理的accum方法更新了count,该方法与服务器进程通信并更新服务器SharedType实例中的count,但您尝试通过对代理的get_custom_argget_builtin_arg方法返回的副本调用方法来更新custom_argbuiltin_arg。修改副本不会将更改通知服务器,也不会对服务器进程中的SharedType实例执行任何操作。


如果您希望能够对get_custom_argget_builtin_arg的返回值进行操作,并使其影响服务器进程中的主对象,则代理的get_custom_argget_builtin_arg也必须返回代理。这意味着您必须注册listCustomArg,并向CustomArg添加方法以读取其数据而无需直接访问v。我认为您可能还必须通过管理器创建builtin_argcustom_arg,或者使用_method_to_typeid_让 SharedType 的代理从get_builtin_argget_custom_arg返回代理 - 我还没有设法在我自己的尝试中解决所有错误,所以我不确定全部细节。


不相关的问题:经理不会消除同步的需要。如果要像这样改变共享对象,则需要使用锁来防止竞争条件。

最新更新