Python 退出时的垃圾收集清理顺序

Python Garbage Collection Cleanup Order On Exit

我有一个存储当前状态信息的上下文。此上下文在 Python 中定义。使用 weakref 的 finalize() 方法处理上下文清理。

class Context:
    def __init__(self, flags = 0):
        weakref.finalize(self, release_ctx, flags)
        self.flags = flags

到目前为止,这一切都很好。最近我添加了一个 C 扩展,其中包含一个使用此上下文中的信息的 C 对象。

typedef struct {
    PyObject_HEAD
    C_CLASS* c_class_that_uses_info_from_py_ctx;
    PyObject* py_ctx;
} PyThing;

在 PyThing 的 c 扩展 init 方法中,我增加了传入的 py_ctx 的引用计数,并在释放时减少了引用计数。我的印象是,只要一个对象的引用计数不为零,它就不会被垃圾回收。当程序退出时,这似乎不是全部。基本上,问题是我需要在依赖于上下文的 C 对象中进行一些清理。但是,在程序退出时,上下文在 C 对象之前被销毁,即使上下文具有非零引用计数(weakref finalize 函数 release_ctx() 在 PyThing 的 dealloc 方法之前被调用,即使打印出 ref在 dealloc 方法中通过 Py_REFCNT 计数 py_ctx 表明它仍然有 5 个引用)。我在这里 https://devguide.python.org/garbage_collector/ 找到一个声明,说如果一个对象不可访问,所有弱引用都会首先处理。我认为这是首先释放 Context 的原因,但我不完全确定。在所有其他对象都被解除分配之前,有没有办法防止这种解除分配发生?我找到了几篇描述 Python 的循环垃圾收集的文章,但我不确定它是否与这个问题有关,如果是,如何使用它来解决它。

经过反复试验,我想我找到了解决问题的方法。我尝试使用 PyThingtp_finalize 插槽注册清理函数,但这并不完全有效。似乎注册到 weakreffinalize 方法的函数在退出时的释放顺序方面具有优先权。因为 Context 正在使用 weakref 的最终确定,所以它在 PyThing 退出之前被清理干净。我需要用 weakref 注册 PyThing 以获得正确的释放顺序。 PyThing 的释放方法正常工作(即它对拥有的上下文执行引用计数递减以及释放 C_CLASS)但我还创建了一个单独的方法来注册 PyThing weakref(在 C api 中的 PyThing 的 init 方法中完成)仅释放 C_CLASS。如果在程序中间引用计数变为 0,则将调用 deallocation 方法,但在退出时,将调用 finalize 方法并在上下文被销毁之前销毁 C_CLASS(deallocation 方法可能仍然是如此调用 C_CLASS 应该检查它是否为 NULL 所以 delete/free 不会被第二次调用)。