是否可以在不锁定的情况下与 NamespaceProxy 和 BaseManager 共享复杂对象?

Posted

技术标签:

【中文标题】是否可以在不锁定的情况下与 NamespaceProxy 和 BaseManager 共享复杂对象?【英文标题】:Is it possible to share a complex object with NamespaceProxy and BaseManager without locking? 【发布时间】:2020-12-06 21:22:48 【问题描述】:

编辑:

我已经设法“删除”了其中一个锁,但是它仍然很慢。有人知道那里的锁在哪里吗?

class NoLock:
    def __init__(self):
        pass
    def __enter__(self):
        return self
    def __exit__(self, foo=None, bar=None, baz=None):
        pass

BaseManager._mutex = NoLock()
BaseProxy._mutex = NoLock()

我知道对于多处理数组,lock=False 有一个选项,但是否可以对复杂对象做同样的事情?例如:

class Foo:
    def __init__(self):
        self._a = 1000
    def get_a(self):
        return self._a

class SharedFoo(NamespaceProxy):
    _exposed_ = ('__getattribute__', '__getattr__', '__setattr__', '__init__', 'get_a')
    def get_a(self):
        callmethod = object.__getattribute__(self, '_callmethod')
        return callmethod('get_a', ())
        
class FooManager(BaseManager):
    pass
if __name__ == '__main__':
    FooManager.register('SharedFoo', Foo, SharedFoo)
    with FooManager() as manager:
        for i in range(1000000):
            a = foo.get_a()
    processes = []

运行foo.get_a() 1000000 次需要几秒钟,这太慢了(在实际程序中我可能不得不访问它数十亿次)。显然这是由获取和释放锁引起的,那么是否可以手动管理锁,以便仅在使用某些功能时才能使程序锁定?

谢谢

【问题讨论】:

【参考方案1】:

我刚刚遇到您的问题(迟到总比没有好)。首先,我不相信您提供的代码可以运行:foo 没有定义。我也不相信功能级别上存在锁定,如下所示。我已将方法 get_a 修改为 (1) 打印出带有当前正在执行的线程 id 的起始消息,(2) 休眠 2 秒,(3) 打印出带有当前线程 id 的结束消息和 (4)最后返回它的值。我创建了一个由 2 个进程组成的处理池,并将一个名为 fooSharedFoo 实例与调用方法 get_a 的工作函数并行提交了两次。开始和结束消息以及总经过时间清楚地表明对get_a 的调用不是单线程通过该方法:

from multiprocessing.managers import BaseManager, NamespaceProxy
from multiprocessing.pool import Pool
from threading import get_ident
import time

class Foo:
    def __init__(self):
        self._a = 1000
    def get_a(self):
        print(f'started get_ident()')
        time.sleep(2)
        print(f'ended get_ident()')
        return self._a

class SharedFoo(NamespaceProxy):
    _exposed_ = ('__getattribute__', '__getattr__', '__setattr__', '__init__', 'get_a')
    def get_a(self):
        callmethod = object.__getattribute__(self, '_callmethod')
        return callmethod('get_a', ())

class FooManager(BaseManager):
    pass


def worker(foo):
    return foo.get_a()


if __name__ == '__main__':
    FooManager.register('SharedFoo', Foo, SharedFoo)
    with FooManager() as manager:
        foo = manager.SharedFoo()
        pool = Pool(2)
        t = time.time()
        pool.apply_async(worker, args=(foo,))
        pool.apply_async(worker, args=(foo,))
        # Wait for tasks to complete:
        pool.close()
        pool.join()
        print(time.time() - t)

打印:

started 22320
started 36068
ended 22320
ended 36068
2.1460039615631104

代理本身很慢。

【讨论】:

以上是关于是否可以在不锁定的情况下与 NamespaceProxy 和 BaseManager 共享复杂对象?的主要内容,如果未能解决你的问题,请参考以下文章

在不激活表单的情况下与表单交互

如何在不缩放场景的情况下与 QGraphicsView 左侧的矩形对齐?

我想知道如何在不通过 chrome 打印弹出窗口的情况下与网络中的打印机进行通信

在不锁定集合的情况下从通用集合中获取 Count 值是不是安全?

如何在不锁定活动方向的情况下锁定片段方向?

在不锁定表的情况下运行 MySQLDump