gpt4 book ai didi

c++ - PySide 中的多线程 Boost Python C++ 代码

转载 作者:塔克拉玛干 更新时间:2023-11-03 02:04:28 26 4
gpt4 key购买 nike

我有用 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 语句(“starting thread”和“Thread started”)中获得输出。

但是 GUI 被卡住了。这与 python GIL 有什么关系吗?我尝试将这些宏添加到 boost_python_wrapper_code.cpp 并重新编译,但它会导致 python GUI 在启动时立即出现段错误。

最佳答案

是的,我相信您的问题与 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 秒,您会注意到您仍然可以在文本字段中键入内容。 sleep 释放 GIL,以便主线程可以继续工作。

当你点击第二个按钮时,线程会做一个重数操作。这会一直获取 GIL,并且 GUI 将锁定直到完成。

如果您的 boost 扩展没有以任何方式对 python 对象执行任何操作,我认为您可以释放 GIL 以进行操作。然后它不会阻止 GUI。如果您正在对 python 对象进行大量操作,那么您可能会在这里遇到问题。

有一个tip on creating a scope-based GIL releasing class你可以在任何函数中使用它来在该函数的持续时间内释放 GIL(当 helper 实例被销毁时)

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);
}

其他人在此 repo 中有更多使用此技巧的示例:https://bitbucket.org/wwitzel3/code/src/tip/nogil/nogil.cpp

关于c++ - PySide 中的多线程 Boost Python C++ 代码,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11991199/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com