子类化multiprocessing.managers.BaseProxy

时间:2017-10-06 11:47:45

标签: python multiprocessing python-multiprocessing subclassing

我在尝试实施新的defaultdict代理对象时遇到了一些麻烦。 documentation有点恐慌,所以我不确定如何正确地解决这个问题。

我想将defaultdict添加到Manager实例中可用的类型列表中。您无法在库存Manager.register上使用multiprocessing.Manager方法,因此我已经从multiprocessing.mangers.BaseManager

创建了自己的存根管理器
class Manager(BaseManager):
    pass

然后我创建了multiprocessing.managers.BaseProxy的子类来容纳defaultdict(我最初尝试使用另一个存根,它将子类化defaultdictBaseProxy但是没有#39}似乎工作。这是我现在拥有的:

class ProxyDefaultDict(BaseProxy):
    def __init__(self, default_factory=None, *args, **kwargs):
        self.__data = defaultdict(default_factory)
        super().__init__(*args, **kwargs)

    def _callmethod(self, methodname, args=(), kwds={}):
        return getattr(self.__data, methodname)(*args, **kwds)

    def _getvalue(self):
        return self.__data.copy()

    def __repr__(self):
        return self.__data.__repr__()

    def __str__(self):
        return self.__data.__str__()

Manager.register('defaultdict', ProxyDefaultDict)

最终目标是拥有一个共享字典,可以跨进程和线程安全地共享密钥锁。以下是我将如何对其进行初始化的示例:

if __name__ == '__main__':
    m = Manager()
    d = m.defaultdict(m.Lock)
    with d['named_lock']:
        print('holding the lock')

但是,我遇到了一些问题:

  1. BaseManager的子类似乎只能通过上下文管理器with Manager() as m来初始化。在这种情况下我会使用m = Manager() - 正如multiprocessing.Manager允许的那样。不是世界末日,而是更好奇为什么会出现这种情况,如果这是一个我不正确做错的迹象。

  2. multiprocessing.managers.BaseManager进行子类化也可以解除multiprocessing.Manager的默认注册值。在这种情况下,我需要为我的经理重新注册ProxyLock(我也不确定预期的方法)。直接继承multiprocessing.Manager是否安全。

  3. 最后,我的ProxyDefaultDict似乎不允许我干净地覆盖其__init__。而且我很遗憾在子类化时不调用BaseProxy.__init__。问题是BaseProxy也接受位置参数。我想这样做的方法是将default_factory参数仅作为一个键控参数,但这会将预期的接口更改为defaultdict,并让我假设我在这里再次做错了。像Manager.Lock这样的其他类型似乎能够接受位置参数。

  4. 感谢您的帮助。

1 个答案:

答案 0 :(得分:5)

查看源代码之后,对它进行一点修改就可以让我获得一个没有问题的defaultdict类型代理(基于如何创建内置的DictProxy)。

seeds

就个人而言,通过使用已经提供的工具,我觉得很方便,甚至不用担心如何自己进行子类化。

但是,我不认为这将允许您创建您正在寻找的默认锁定方案。多处理锁被设计为仅被继承,并且通常不能对Lock进行pickle,这是通过代理传输的数据类型的要求。示例:

from collections import defaultdict

from multiprocessing.managers import MakeProxyType, SyncManager

DefaultDictProxy = MakeProxyType("DefaultDictProxy", [
    '__contains__', '__delitem__', '__getitem__', '__len__',
    '__setitem__', 'clear', 'copy', 'default_factory', 'fromkeys',
    'get', 'items', 'keys', 'pop', 'popitem', 'setdefault',
    'update', 'values'])

SyncManager.register("defaultdict", defaultdict, DefaultDictProxy)
# Can also create your own Manager here, just using built in for simplicity

if __name__ == '__main__':
    with SyncManager() as sm:
        dd = sm.defaultdict(list)
        print(dd['a'])
        # []

会引发运行时错误:

    from multiprocessing import Lock

    m = SyncManager()
    m.start()
    d = m.defaultdict(Lock)
    print(d['named_lock'])
    m.shutdown()