Python多处理嵌套共享对象不适用于Queue

时间:2019-06-22 14:53:57

标签: python memory-management process multiprocessing queue

multiprocessing模块状态的Python文档:

  

共享对象可以嵌套。例如,共享容器对象(例如共享列表)可以包含其他共享对象,这些对象都将由SyncManager管理和同步。

这确实适用于listdict。但是,如果我尝试在共享的Queue中创建共享的dict,则会收到错误消息:

>>> from multiprocessing import Manager
>>> m = Manager()
>>> d = m.dict()
>>> d['a'] = m.list()
>>> d['b'] = m.dict()
>>> d['c'] = m.Queue()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 2, in __setitem__
  File "/usr/lib/python3.6/multiprocessing/managers.py", line 772, in _callmethod
    raise convert_to_error(kind, result)
multiprocessing.managers.RemoteError: 
---------------------------------------------------------------------------
Traceback (most recent call last):
  File "/usr/lib/python3.6/multiprocessing/managers.py", line 228, in serve_client
    request = recv()
  File "/usr/lib/python3.6/multiprocessing/connection.py", line 251, in recv
    return _ForkingPickler.loads(buf.getbuffer())
  File "/usr/lib/python3.6/multiprocessing/managers.py", line 881, in RebuildProxy
    return func(token, serializer, incref=incref, **kwds)
TypeError: AutoProxy() got an unexpected keyword argument 'manager_owned'
---------------------------------------------------------------------------

似乎https://hg.python.org/cpython/rev/39e7307f9aee是引入嵌套共享对象的变更集。

2 个答案:

答案 0 :(得分:1)

该错误是由AutoProxy当前未处理所有BaseProxy自变量引起的。有一个pull request尚未合并。您要么需要猴子AutoProxy补丁,要么调查multiprocessing.managers.py并将补丁here中的更改直接应用于源代码。

修复补丁中的两行以防止服务器进程中的内存泄漏非常重要。 manager_owned标志用于通知BaseProxy代码,何时跳过管理者拥有的代理的引用增量(通过嵌套)。

答案 1 :(得分:1)

如果不想修补基础python库,则可以使用以下代码在自己的代码中应用此修补程序。

我已经从@Darkonaut的pull request参考中复制了更改,并进行了软件包名称的修改,因此它可以在原始软件包之外使用。它将放置在使用multiprocessing.managers的任何模块的模块级别。

请注意,@ Darkonaut的答案中的solution I referenced注释在我自己的测试中产生了段错误,但是此解决方案却没有。

import multiprocessing.managers

def AutoProxy(token, serializer, manager=None, authkey=None,
              exposed=None, incref=True, manager_owned=False):
    '''
    Return an auto-proxy for `token`
    '''
    _Client = multiprocessing.managers.listener_client[serializer][1]

    if exposed is None:
        conn = _Client(token.address, authkey=authkey)
        try:
            exposed = dispatch(conn, None, 'get_methods', (token,))
        finally:
            conn.close()

    if authkey is None and manager is not None:
        authkey = manager._authkey
    if authkey is None:
        authkey = multiprocessing.process.current_process().authkey

    ProxyType = multiprocessing.managers.MakeProxyType('AutoProxy[%s]' % token.typeid, exposed)
    proxy = ProxyType(token, serializer, manager=manager, authkey=authkey,
                      incref=incref, manager_owned=manager_owned)
    proxy._isauto = True
    return proxy

multiprocessing.managers.AutoProxy = AutoProxy