我遇到了一个很难解决的Cython错误。 我有一个struct ret_val,它有一个名为last_visited的long [:]字段。 我正在尝试设置此但得到以下运行时错误:
class AsyncOperation extends Thread
{
public function __construct($threadId)
{
$this->threadId = $threadId;
}
public function run()
{
printf("T %s: Sleeping 3sec\n", $this->threadId);
sleep(3);
printf("T %s: Hello World\n", $this->threadId);
}
}
$start = microtime(true);
for ($i = 1; $i <= 5; $i++) {
$t[$i] = new AsyncOperation($i);
$t[$i]->start();
}
echo microtime(true) - $start . "\n";
echo "end\n";
以下是上述行负责人C档案的摘录:
Fatal Python error: Acquisition count is -1753032536 (line 5052)
我尝试制作一个最小的例子来重现错误,但后来没有发生。然后我重写了使用最小例子绘制的函数,它再次失败。
这是一个不会产生错误的最小示例,但据我所知,它在功能上与导致错误的代码相同:
/* "cymain.pyx":196
* last = np.array([1,1,1], dtype=np.int64)
* ret_val.last_visited = last # <<<<<<<<<<<<<<
*/
__pyx_t_9 = __Pyx_PyObject_to_MemoryviewSlice_ds_long(__pyx_v_last);
if (unlikely(!__pyx_t_9.memview)) {
__pyx_filename = __pyx_f[0];
__pyx_lineno = 196;
__pyx_clineno = __LINE__;
goto __pyx_L1_error;
}
__PYX_XDEC_MEMVIEW(&__pyx_v_ret_val->last_visited, 0);
__pyx_v_ret_val->last_visited = __pyx_t_9;
__pyx_t_9.memview = NULL;
__pyx_t_9.data = NULL;
然后致电
cdef struct baz:
long[:] lv
othermodule.something* cd
cdef baz* initialise_baz(dict req):
cdef:
baz* ret_val = <baz *> malloc(sizeof(baz))
long nlevels = 3
ret_val.cd = NULL
lv = req["key"]
lv = np.array(lv, dtype=np.int64)
ret_val.lv = lv
return ret_val
def test_memview_error(req):
cdef baz* foo
foo = initialise_baz(req)
print "foo.lv[2]", foo.lv[2]
答案 0 :(得分:1)
我认为问题与未初始化的记忆有关(正如我在评论中所说)。查看简单示例中生成的C代码:
/* "code.pyx":5
* import numpy as np
*
* cdef struct baz: # <<<<<<<<<<<<<<
* long[:] lv
* #othermodule.something* cd
*/
struct __pyx_t_4code_baz {
__Pyx_memviewslice lv;
};
(请注意,为简单起见,我已注释掉othermodule.something
。 __Pyx_memviewslice
定义为
typedef struct {
struct __pyx_memoryview_obj *memview;
char *data;
Py_ssize_t shape[8];
Py_ssize_t strides[8];
Py_ssize_t suboffsets[8];
} __Pyx_memviewslice;
来自initialise_baz
的一些相关代码(我在这里略过了一些内容)
__pyx_v_ret_val = ((struct __pyx_t_4code_baz *)malloc((sizeof(struct __pyx_t_4code_baz))));
请注意,malloc
不会(必然)将内存归零。因此,lv
(关键是指向memview
的指针)的内容被设置为任意内容(可能是之前的内存 - 这显然取决于您之前运行的其他代码)。如果您使用calloc
而不是malloc
,它会将内存归零。 initialise_baz
继续:
/* "code.pyx":18
* lv = req["key"]
* lv = np.array(lv, dtype=np.int64)
* ret_val.lv = lv # <<<<<<<<<<<<<<
*
* return ret_val
*/
__pyx_t_6 = __Pyx_PyObject_to_MemoryviewSlice_ds_long(__pyx_v_lv);
if (unlikely(!__pyx_t_6.memview)) {__pyx_filename = __pyx_f[0]; __pyx_lineno = 18; __pyx_clineno = __LINE__; goto __pyx_L1_error;}
__PYX_XDEC_MEMVIEW(&__pyx_v_ret_val->lv, 0);
__pyx_v_ret_val->lv = __pyx_t_6;
__pyx_t_6.memview = NULL;
__pyx_t_6.data = NULL;
关键行__PYX_XDEC_MEMVIEW
在lv
的前一个(任意!)内容上调用。这就是出问题的地方。 lv.memview
指向一个任意位置,从中可以读出它认为是acquisition_count
的内容。
表面修复是使用calloc
而不是malloc
。但是,即使在这种情况下lv
在释放baz
时也永远不会被正确释放,这可能会导致内存泄漏。我确实认为将内存视图用作C结构的一部分并不合理。您是否可以将它们作为cdef class
的一部分使用,其中一切都得到妥善处理?