0

我在使用 Python C API 时遇到了一个非常奇怪的问题。我有一个全局范围的结构 'dcon',其中包含一个 PyObject 'device' 成员。

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 有点陌生。我最初的想法是我在引用计数方面做错了,但我似乎无法理解它。

4

1 回答 1

1

在多线程应用程序中,任何调用 Python C API 函数的线程必须首先确保该线程持有全局解释器锁。对于从 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() 的匹配调用,即使已经获得了锁,也可以使用这些调用。

于 2012-10-28T08:42:22.240 回答