2

単純な C++ クラスがあるとします。

class Widget
{
public:
    Widget() :
            x(1)
    { }

    void sleep()
    {
        sleep(x);
    }

private:
    int x;
};

Widget::sleepブロックなので、Python が他のことをできるように GIL を解放したいので、次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()都合よく削除されます。pyWidgetSleepGIL を解放してからdel c.x、インスタンスへの最後の参照をデクリメントし、Widget未定義の動作を引き起こす可能性があります。


私のマシンではこのブレークを作成できません。これは、期待どおりに動作することを示す良い兆候のようですが、参照カウントはこの程度明確に文書化されていません (または、少なくとも、できる.見つからないようです)。CPython の関連部分は のように見えます。これは、関数呼び出しに関係するすべての引数にPyEval_EvalCodeEx適用されるように見えますが、私はそれについて完全にオフになっている可能性があります。Py_INCREF

4

1 に答える 1

0

この質問に対する答えは「はい、安全です」です。これは、Python でメンバー関数が呼び出される方法の結果として発生します。WidgetPython で実装されているかどうかを検討してください。

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

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

への最初のパラメータがどのようになっていることに注意してWidget.sleepくださいself。参照カウントは、呼び出し中にインクリメントされます! 振り返ってみると一目瞭然…

于 2012-10-18T16:50:11.873 に答える