Python2.7文档的两部分提到了为扩展模块中定义的容器对象添加循环垃圾收集(cgc)支持。
Python/C API Reference Manual给出了两个规则,即,
必须使用PyObject_GC_New()PyObject_GC_NewVar()分配对象的内存。
一旦初始化所有可能包含对其他容器引用的字段,它必须调用PyObject_GC_Track()
而在Extending and Embedding the Python Interpreter中,对于Noddy示例,添加Py_TPFLAGS_HAVE_GC标志并填充tp_traversetp_clear插槽似乎足以启用cgc支持。上面的两条规则根本就没有被实践过。
当我修改Noddy示例以实际遵循PyObject_GC_New()/PyObject_GC_Del()PyObject_Track()/PyObject_GC_UnTrack()的规则时,它意外地引发断言错误说,
modules/gcmodule.c:348:visit_decref:assertion“gc->gc.gc参考!=0“失败。引用计数太小
这导致了我对实现cgc的正确/安全方法的困惑。有谁能给出建议,或者,最好是一个有cgc支持的容器对象的简洁示例?

最佳答案

在大多数情况下,你不需要自己去做追踪/去追踪。这是在文件中描述的,但是没有具体说明。如果是Noddy example你肯定不会。
简短的版本是,typeobject包含两个函数指针:tp_alloctp_free。默认情况下,tp_alloc在创建类时调用所有正确的函数(如果设置了Py_TPFLAGS_HAVE_GC),而tp_free在销毁时取消对类的跟踪。
Noddy documentation says(在本节末尾):
差不多就是这样。如果我们已经编写了自定义的tp_alloctp_free插槽,那么我们需要修改它们以进行循环垃圾收集。大多数扩展将使用自动提供的版本。
不幸的是,一个地方不清楚你不需要自己做这件事就是Supporting Cyclic Garbage Collection documentation
细节:
noddy的分配使用一个名为Noddy_new的函数,该函数放在tp_newTypeObject插槽中。根据the documentation,“new”函数应该做的主要事情是调用tp_alloc slot。您通常不自己编写tp_alloc,它只是默认为PyType_GenericAlloc()
查看PyType_GenericAlloc() in the Python source显示了许多部分,其中它根据PyType_IS_GC(type)进行了更改。首先它调用_PyObject_GC_Malloc而不是PyObject_Malloc,其次它调用_PyObject_GC_TRACK(obj)。[注意PyObject_New真正做的就是调用PyObject_Malloc然后tp_init]
类似地,在解除分配时,调用tp_free slot,对于具有PyObject_GC_Del的类,它会自动设置为Py_TPFLAGS_HAVE_GCPyObject_GC_Del包含与PyObject_GC_UnTrack相同的代码,因此不需要调用取消跟踪。

08-17 02:28