我正在编写一个Python类,它将包含一个包含C结构的C模块。我使用的是Cython语言(Python和C的超集语言)。 C结构是构造函数中的malloc,包含一个我想在Python中使用的数组。该数组将在Python中表示为NumPy数组,但我不想将值复制到它。我想将NumPy数组直接链接到malloc内存。对于此任务,我使用NumPy Array API,特别是此函数:
PyObject*
PyArray_SimpleNewFromData
(int nd, npy_intp* dims, int typenum, void* data)
我设法在Cython中使用此代码将NumPy数组绑定到C结构的数组,只要NumPy数组和MultimediaParams
对象具有相同的生命周期,它就可以正常工作:
cdef class MultimediaParams:
def __init__(self, **kwargs):
self._mm_np = < mm_np *> malloc(sizeof(mm_np))
#some code...
def as_ndarray(self): #TODO: what if self deallocated but numpy array still exists(segfault?)
cdef numpy.npy_intp shape[1]
cdef int arr_size = sizeof(self._mm_np[0].n2) / sizeof(self._mm_np[0].n2[0])
shape[0] = < numpy.npy_intp > arr_size
cdef numpy.ndarray ndarray
ndarray = numpy.PyArray_SimpleNewFromData(1, shape, numpy.NPY_DOUBLE, self._mm_np[0].n2)
return ndarray
def __dealloc__(self):
free(self._mm_np)
正如您所看到的,该类具有__dealloc__
方法,该方法将处理在C中分配的内存,并在没有对MultimediaParams
实例的引用时释放它。
在这种绑定中,NumPy不拥有数组的内存。
问题:当释放MultimediaParams
对象并释放数组的内存时,NumPy对象仍然指向刚刚释放的内存。当NumPy对象尝试访问/修改已释放的内存时,这将导致段错误。
如果只有NumPy对象使用其内存,我怎样才能确保MultimediaParams
对象没有被释放?
据我了解,我需要做的就是让NumPy对象对MultimediaParams
实例进行引用,从中获取内存指向的实例。
我尝试使用ndarray.base = <PyObject*>self
,因此NumPy将知道它的基础对象,这应该添加对MultimediaParams
实例的另一个引用,并且只要NumPy数组处于活动状态,就会导致它不被释放。此行导致我的测试失败,因为NumPy数组的内容变为垃圾。
清除: NumPy阵列不占用C阵列内存的所有权而且我不想要它。我想要MultimediaParams
来负责释放C结构(包含数组数据),但只要NumPy对象存活就不要这样做。
有什么建议吗?
答案 0 :(得分:2)
正如@ JFSebastian的评论所指出的那样,问题很可能是当您正确地将指向MultimediaParams
实例的指针指向NumPy数组的base
引用时,实际上增加了它的引用计数,因为赋值是用C语言编写的,而不是用Python语言编写的。这可能导致MultimediaParams
对象的过早垃圾收集,其内存被重用并导致您在ndarray中作为垃圾数据体验。
使用宏MultimediaParams
手动递增Py_INCREF
对象的引用计数应该会产生所需的行为。