2

假设我有一个简单的 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.fooc,后面的行t.start()方便地删除。由于pyWidgetSleep释放 GIL,del c.x可能会减少对Widget实例的最后引用,从而导致未定义的行为。


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

4

1 回答 1

0

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

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

    def sleep(self):
        os.sleep(self.x)

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

于 2012-10-18T16:50:11.873 回答