假设我有一个简单的 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
吗?引用计数在调用期间递增!回想起来似乎很明显......
关于 python GIL : Do all participants in an expression have ref count incremented for the duration of the expression?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/12945457/