为什么 __del__ 方法没有被调用?

Why isn't the __del__ method called?

在python3的multiprocess中,无法调用__del__方法。

我看过其他关于循环引用的问题,但我找不到multiprocess中的情况。

foo中存在循环引用,直接调用foo时会调用__del__,而multiprocess中永远不会调用__del__被调用。

import multiprocessing
import weakref


class Foo():
    def __init__(self):
        self.b = []

    def __del__(self):
        print ('del')


def foo():
    print ('call foo')
    f = Foo()
    a = [f]
    # a = [weakref.ref(f)]
    f.b.append(a)


# call foo in other process
p = multiprocessing.Process(target=foo)
p.start()
p.join()


# call foo
foo()

输出:
调用 foo
调用 foo
删除

为什么 p 中没有调用 __del__

Forked Process objects 在 运行 使用 os._exit() 完成他们的任务后终止,这会强制终止 child 进程 而无需 正常清理 Python 在退出时执行。循环垃圾没有被清理(因为进程在没有给循环 GC 机会 运行 的情况下终止),它只是掉在地上,留下 OS 清理。

这是故意的,因为正常退出(调用所有正常的清理程序)会冒一些风险,比如未刷新的缓冲区在 parent 和 child 中被刷新(双倍输出),以及其他奇怪的情况分叉进程继承了 parent 的所有状态,但不应使用它,除非明确告知要这样做。

问题实际上不是为什么 没有 multiprocess 中调用,而是为什么 在 中调用另一个例子。答案是当您调用 foo 时它不会被调用。它在程序结束时调用。由于程序已完成,Python知道即使它仍然被引用,也可以清除其他任何内容,因此它会清除循环引用。

如果你在脚本末尾添加一个 print 语句,或者从 REPL 调用它,你可以看到 __del__ 仍然没有在你的第二个 foo 调用其中一个,但只能在脚本末尾调用。

鉴于 Python 在脚本结束时清除循环引用, 解释了为什么在多处理函数完成时没有发生这种情况。