我遇到了Python C API的一个非常奇怪的问题。我有一个全局范围的结构'dcon',它包含一个PyObject'设备'成员。
static status_t get_mac_addr(uint8_t const ** addr,
size_t * const size){
static uint8_t device_mac_addr[6] = {0};
*addr = device_mac_addr;
*size = sizeof device_mac_addr;
if(PyObject_HasAttrString(dcon.device, "mac_address") == 1){
...
}
return 0;
}
似乎每当'mac_address'属性存在时,代码执行就好了。奇怪的是,当'mac_address'不是定义的属性时,我会遇到一个segault(Segmentation fault (core dumped)
)。这是遇到段错误时的回溯:
(gdb) bt
#0 0x00000000004ca607 in PyErr_Restore ()
#1 0x00000000004aa29f in PyErr_Format ()
#2 0x000000000049e6ee in _PyObject_GenericGetAttrWithDict ()
#3 0x0000000000486387 in PyObject_GetAttrString ()
#4 0x00000000004ea7d7 in PyObject_HasAttrString ()
#5 0x00007ffff4f2056d in get_mac_addr (size=0x7ffff4f1cd28, addr=<optimized out>) at config.c:165
我对Python C API有点新手。我最初的想法是,我在引用计数方面做错了,但我似乎无法绕过它。
答案 0 :(得分:1)
在多线程应用程序中,任何调用Python C API函数的线程必须首先确保该线程持有Global Interpreter Lock。对于从Python代码调用的函数,这不是问题,因为锁已经被保存。
从解释器外部调用的代码(回调等)不是这种情况。
在这种情况下,必须先获取GIL,然后才能安全地调用Python API函数。
PyGILState_STATE gstate;
gstate = PyGILState_Ensure();
...
PyGILState_Release(gstate);
这不仅可以确保GIL被保留,而且还可以为在Python之外创建的线程创建Python线程状态(使用直接OS调用,而不是thread.start_new_thread()
)。
在调用Python API时可能发生的任何Python异常都必须在发布GIL之前处理。
请注意,如果另一个线程当前持有锁(例如因为它正在执行Python代码),则Ensure调用将阻塞,直到另一个线程释放锁。
即使已经获取了锁,也可以使用这些调用,前提是每次调用Ensure()都有对Release()的匹配调用。