4

Boost Python でラップしている C++ コードがあります。アイデアは、Python GUI が C++ 機能をラップする変数をインスタンス化するために使用できる共有オブジェクトを作成することです。

C++ コードはいくつかの処理を行うため、GUI がブロックされないように、ラップされたオブジェクトを同時に実行できるようにしたいと考えています。

次のように CMake でラップされた Boost Python 共有オブジェクトをコンパイルしました。

find_package(Boost COMPONENTS system thread python REQUIRED)
find_package(PythonLibs REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
include_directories(${PYTHON_INCLUDE_DIRS})
link_directories(${Boost_LIBRARY_DIRS})
link_directories(${PYTHON_LIBRARIES})
set(Boost_USE_MULTITHREADED ON)
add_library(mynewlibinpython SHARED src/boost_python_wrapper_code.cpp)
target_link_libraries(mynewlibinpython ${Boost_LIBRARIES} ${PYTHON_LIBRARIES} mylibincpp)

Python でオブジェクトをインスタンス化することはできますが、GUI がブロックされます。これが私のPythonコードの一般化されたスニペットです:

from mynewlibinpython import *
class CppWrapper(QtCore.QObject):
    def __init__(self):
         super(CppWrapper,self).__init__()
         #some more initialization...

    @QtCore.Slot()
    def heavy_lifting_cpp_invocation():
         #constructor that takes a long time to complete
         self.cpp_object = CppObject()

GUIのボタンを押すとトリガーされるメンバー関数を持つオブジェクトで:

class GuiObjectWithUnimportantName:
     def __init__(self):
          #inititalization

          self.cpp_thread = QThread()
          self.cpp_wrapper = CppWrapper()

     def function_triggered_by_button_press(self):
          self.cpp_wrapper.moveToThread(self.cpp_thread)
          self.cpp_thread.started.connect(self.cpp_wrapper.heavy_lifting_cpp_invocation)
          print "starting thread"       
          self.cpp_thread.start()
          print "Thread started"      

したがって、cpp コードはその処理を開始し、基本的に両方のprintステートメント (「開始スレッド」と「スレッド開始」) から出力をすぐに取得します。

しかし、GUI はフリーズしています。これはpython GILと関係がありますか? これらのマクロをに追加してboost_python_wrapper_code.cpp再コンパイルしようとしましたが、起動時にすぐに python GUI が segfault します。

4

1 に答える 1

3

はい、あなたの問題は GIL に関連していると思います。何が起こっているかの簡単な例を次に示します。

from PyQt4 import QtCore, QtGui
import time

class Window(QtGui.QDialog):

    def __init__(self):
        super(Window, self).__init__()
        self.resize(200,100)

        layout = QtGui.QVBoxLayout(self)

        layout.addWidget(QtGui.QLineEdit())

        self.button1 = QtGui.QPushButton("Sleep")
        self.button1.clicked.connect(self.go_safe)
        layout.addWidget(self.button1)

        self.button2 = QtGui.QPushButton("Crunch")
        self.button2.clicked.connect(self.go_GIL_buster)
        layout.addWidget(self.button2)

    def go_safe(self):
        t = QtCore.QThread(self)
        heavy = Heavy()
        heavy.moveToThread(t)
        t.started.connect(heavy.safe)
        print "starting thread"
        t.start()
        print "thread started"
        self.t1 = t
        self.heavy1 = heavy

    def go_GIL_buster(self):
        t = QtCore.QThread(self)
        heavy = Heavy()
        heavy.moveToThread(t)
        t.started.connect(heavy.GIL_buster)
        print "starting thread"
        t.start()
        print "thread started"
        self.t2 = t
        self.heavy2 = heavy

class Heavy(QtCore.QObject):

    def safe(self):
        print "Sleeping"
        time.sleep(10)
        print "Done"

    def GIL_buster(self):
        print "Crunching numbers"
        x = 2
        y = 500000000
        foo = x**y
        print "Done"


if __name__ == "__main__":
    app = QtGui.QApplication([])
    win = Window()
    win.show()
    win.raise_()
    app.exec_()

最初のボタンをクリックすると、スレッドは 10 秒間スリープしますが、テキスト フィールドに入力できることがわかります。スリープにより GIL が解放されるため、メイン スレッドは引き続き機能します。

2 番目のボタンをクリックすると、スレッドは重い数値演算を実行します。これは常に GIL を取得し、それが完了するまで GUI はロックされます。

ブースト拡張機能が python オブジェクトで何もしていない場合は、その操作のために GIL を解放できると思います。その後、GUIをブロックしません。Python オブジェクトに対して負荷の高い操作を行っている場合は、ここで問題が発生する可能性があります。

任意の関数で使用できるスコープベースの GIL 解放クラスの作成に関するヒントがあり、その関数の実行中 (ヘルパー インスタンスが破棄されたとき) に GIL を解放します。

class ScopedGILRelease
{
// C & D ----------------------------
public:
    inline ScopedGILRelease()
    {
        m_thread_state = PyEval_SaveThread();
    }

    inline ~ScopedGILRelease()
    {
        PyEval_RestoreThread(m_thread_state);
        m_thread_state = NULL;
    }

private:
    PyThreadState * m_thread_state;
};

そしてそれを使用して...

int foo_wrapper(int x)
{
    ScopedGILRelease scoped;
    return foo(x);
}

他の誰かが、このレポでこのヒントを使用した例をいくつか持っています: https://bitbucket.org/wwitzel3/code/src/tip/nogil/nogil.cpp

于 2012-08-16T20:40:07.670 に答える