使用具有 multiprocessing.Manager 的上下文管理器有什么好处?

What is the benefit of using a context mananger with multiprocessing.Manager?

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 ...: 块打开时分配和获取的。它在块结束时被释放(到达末尾或因为引发异常)。每当垃圾收集器找到它时,它仍然会被释放,但由于外部资源已经被释放,所以这不太重要。

multiprocessing.Manager 的情况下(这是一个 returns 和 SyncManager 的函数,尽管 Manager 看起来很像 class),资源是一个 "server" 进程,它保存状态和一些共享该状态的工作进程。

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

如果您不使用上下文管理器并且不在管理器上调用关闭,那么 "server" 进程将继续 运行ning 直到 SyncManager__del__ 是 运行。在某些情况下,这可能会在创建 SyncManager 的代码完成后不久发生(例如,如果它是在一个短函数内创建的,而函数 returns 正常并且您正在使用 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?

您必须自己实施上下文管理器协议,就像您在没有 with 的情况下使用的任何上下文管理器一样。在 pure-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)

startshutdown也分别是__enter____exit__的别名。