Skip to main content
 首页 » 编程设计

python GIL : Do all participants in an expression have ref count incremented for the duration of the expression

2025年05月04日10小虾米

假设我有一个简单的 C++ 类:

class Widget 
{ 
public: 
    Widget() : 
            x(1) 
    { } 
 
    void sleep() 
    { 
        sleep(x); 
    } 
 
private: 
    int x; 
}; 

Widget::sleep block ,所以我想释放 GIL 以便 Python 可以做一些其他的事情,所以我将 Widget::sleep 包装在一个像这样的功能:

static void pyWidgetSleep(Widget& self) 
{ 
    PyThreadState* state = PyEval_SaveThread(); 
    self.sleep(); 
    PyEval_RestoreThread(state); 
} 

为了完整起见,绑定(bind)代码如下所示:

BOOST_PYTHON_MODULE(libnative) 
{ 
    boost::python::class_<Widget>("Widget") 
        .def("sleep", &pyWidgetSleep); 
} 

我有一个简单的 Python 脚本,如下所示:

import libnative 
import threading 
 
class C: 
    def __init__(self): 
        self.x = libnative.Widget() 
        self.sleeping = False 
 
    def foo(self): 
        self.sleeping = True 
        self.x.sleep() 
 
c = C() 
t = threading.Thread(target=c.foo) 
t.start() 
while not c.sleeping: 
    pass 
del c.x 

表达式中的所有参与者是否都在表达式持续时间内增加了他们的引用计数? c.x 在 C.foo 中使用时唯一引用的是 c,t.start() 之后的行> 随手删除。 由于 pyWidgetSleep 释放 GIL,del c.x 可能会减少对 Widget 实例的最后引用,从而导致未定义的行为。


我无法在我的机器上进行此中断,这似乎很好地表明它按我预期的方式工作,但引用计数并未记录到如此清晰的程度 (或者,至少,我似乎找不到它)。 CPython 的相关部分似乎是 PyEval_EvalCodeEx,它看起来像是将 Py_INCREF 应用于所有参数 涉及一个函数调用,但我可能完全不关心那个。

请您参考如下方法:

这个问题的答案是是的,它是安全的。这是由于在 Python 中调用成员函数的方式造成的。考虑是否 Widget 是用 Python 实现的:

class Widget: 
    def __init__(self): 
        self.x = 1 
 
    def sleep(self): 
        os.sleep(self.x) 

注意 Widget.sleep 的第一个参数是 self 吗?引用计数在调用期间递增!回想起来似乎很明显......