假设我有一个简单的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
实例的最后一次引用,从而导致未定义的行为。
我不能在我的机器上做这个休息,这似乎是一个很好的迹象表明它可以按照我的预期工作,但参考计数没有记录到这个程度的清晰度
(或者,至少,我似乎无法找到它)。 CPython的相关部分似乎是PyEval_EvalCodeEx
,看起来它将Py_INCREF
应用于所有参数
参与一个函数调用,但我可能完全不参与。
答案 0 :(得分:0)
这个问题的答案是是的,它是安全的。这是因为在Python中调用成员函数的结果。考虑是否在Python中实现了Widget
:
class Widget:
def __init__(self):
self.x = 1
def sleep(self):
os.sleep(self.x)
注意Widget.sleep
的第一个参数是self
的方式?引用计数在呼叫期间递增!回想起来似乎很明显......