假设我有一个简单的 C++ 类:
class Widget
{
public:
Widget() :
x(1)
{ }
void sleep()
{
sleep(x);
}
private:
int x;
};
Widget::sleep
块,所以我想释放 GIL 以便 Python 可以做一些其他的事情,所以我包装Widget::sleep
在一个这样的函数中:
static void pyWidgetSleep(Widget& self)
{
PyThreadState* state = PyEval_SaveThread();
self.sleep();
PyEval_RestoreThread(state);
}
为了完整起见,绑定代码如下所示:
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
实例的最后引用,从而导致未定义的行为。
我无法在我的机器上进行此中断,这似乎很好地表明它可以按我的预期工作,但引用计数没有记录到这种清晰程度(或者,至少,我可以' t 似乎找到它)。CPython 的相关部分似乎是PyEval_EvalCodeEx
,看起来它适用Py_INCREF
于函数调用中涉及的所有参数,但我可能完全不关心那个。