Skip to main content
 首页 » 编程设计

python之将上下文管理器与 multiprocessing.Manager 一起使用有什么好处

2025年05月04日20cmt

在the documentation ,管理器与上下文管理器一起使用(即 with),如下所示:

from multiprocessing.managers import BaseManager 
 
class MathsClass: 
    def add(self, x, y): 
        return x + y 
    def mul(self, x, y): 
        return x * y 
 
class MyManager(BaseManager): 
    pass 
 
MyManager.register('Maths', MathsClass) 
 
if __name__ == '__main__': 
    with MyManager() as manager: 
        maths = manager.Maths() 
        print(maths.add(4, 3))         # prints 7 
        print(maths.mul(7, 8))         # prints 56 

但是除了命名空间之外,这样做有什么好处呢?对于打开文件流,好处是非常明显的,因为您不必手动.close() 连接,但它对Manager 有什么用呢?如果您不在上下文中使用它,您必须使用哪些步骤来确保一切都正确关闭?

简而言之,使用上面的方法比类似的方法有什么好处:

manager = MyManager() 
maths = manager.Maths() 
print(maths.add(4, 3))         # prints 7 
print(maths.mul(7, 8))         # prints 56 

请您参考如下方法:

But what is the benefit of this (...)?

首先,您可以获得几乎所有上下文管理器的主要好处。您有明确定义的资源生命周期。它是在 with ...: block 打开时分配和获取的。它在 block 结束时被释放(到达末尾或因为引发异常)。只要垃圾收集器绕过它,它仍然会被释放,但由于外部资源已经被释放,所以这不太重要。

在 multiprocessing.Manager 的情况下(这是一个返回 SyncManager 的函数,尽管 Manager 看起来很像一个类) ,该资源是一个保存状态的“服务器”进程和共享该状态的多个工作进程。

what is [the benefit of using a context manager] for Manager?

如果您不使用上下文管理器并且不在管理器上调用关闭,那么“服务器”进程将继续运行,直到 SyncManager 的 __del__ 运行。在某些情况下,这可能会在创建 SyncManager 的代码完成后不久发生(例如,如果它是在一个短函数内创建的并且该函数正常返回并且您正在使用 CPython 那么引用计数系统可能会很快注意到对象已死并调用它的 __del__)。在其他情况下,它可能需要更长的时间(如果引发异常并保留对管理器的引用,那么它将保持事件状态直到处理该异常)。在某些糟糕的情况下,它可能根本不会发生(如果 SyncManager 结束在一个引用循环中,那么它的 __del__ 将完全阻止循环收集器收集它 ;否则您的进程可能会在调用 __del__ 之前崩溃)。在所有这些情况下,您将放弃控制何时清理 SyncManager 创建的额外 Python 进程。这些进程可能代表您系统上的重要资源使用情况。在非常糟糕的情况下,如果您在循环中创建 SyncManager,您最终可能会创建许多同时存在的此类,并且很容易消耗大量资源。

If you don't use it in a context, what steps do you have to use to ensure that everything is closed properly?

您必须自己实现上下文管理器协议(protocol),就像您在没有使用 with 的情况下使用的任何上下文管理器一样。在纯 Python 中同时保持正确是很棘手的。像这样的东西:

manager = None 
try: 
    manager = MyManager() 
    manager.__enter__() 
    # use it ... 
except: 
    if manager is not None: 
        manager.__exit__(*exc_info()) 
else: 
    if manager is not None: 
        manager.__exit__(None, None, None) 

start 和 shutdown 也分别是 __enter__ 和 __exit__ 的别名。