我创建了一个简单的GUI应用程序,并希望在工作线程上运行更长时间的操作而不是事件调度线程来维护GUI响应。
只要工作线程正在运行python函数,这就可以正常工作。但是,如果函数在C ++代码上执行某些操作,则GUI线程会阻塞,就像对其执行延长操作而不是在工作线程上执行!
看起来好像有一些隐藏锁阻止GUI线程与工作线程并行运行。
我怀疑它可能与正在使用的UI框架有关,所以我尝试了TKinter和wxPython,问题就出现了。
我在Windows 7上使用Visual Studio 2010和python 2.7.5
这是C ++代码:
注意:我也试过非忙等待睡眠(timeSec * 1000L),行为相同
#include<boost/python.hpp>
#include <ctime>
void busyWait(int timeSec) {
clock_t beginTime(clock());
while (clock() - beginTime < timeSec * 1000L);
}
using namespace boost::python;
BOOST_PYTHON_MODULE(BusyCpp) {
def("busyWait", &busyWait, "waits...");
}
这就是python代码
# Set the path to append the DLL to it
from Utils import ToolEnvironment
ToolEnvironment.useToolBinaries()
from threading import Thread
import Tkinter
import BusyCpp
class simpleapp_tk(Tkinter.Tk):
def __init__(self, parent):
Tkinter.Tk.__init__(self, parent)
self.waitPeriod = 5 # seconds
button1 = Tkinter.Button(self, text=u"Busy C++", command=self.OnBusyCppClick)
button1.place(x=20, y=20)
button2 = Tkinter.Button(self, text=u"Busy Python", command=self.OnBusyPyClick)
button2.place(x=20, y=60)
def OnBusyCppClick(self):
t = Thread(target=self.busyWaitCpp)
t.start()
print 'Started thread'
def OnBusyPyClick(self):
t = Thread(target=self.busyWaitPy)
t.start()
print 'Started thread'
def busyWaitCpp(self):
BusyCpp.busyWait(self.waitPeriod)
print 'Done waiting C++'
def busyWaitPy(self):
from time import time
beginTime = time()
while time() - beginTime < self.waitPeriod:
pass
print 'Done waiting python'
if __name__ == "__main__":
app = simpleapp_tk(None)
app.title('my application')
app.mainloop()
当点击“忙碌的Python”按钮时,可以看到GUI响应(按钮看起来没有被点击),这些打印输出按此顺序出现,其中“已启动的线程”立即出现,如预期的那样:
Started thread
Done waiting python
单击“忙碌的C ++”按钮时,可以看到GUI没有响应(按钮在等待时看起来像点击一样),这些打印输出按此顺序显示,两者都只出现一次等待结束:
Done waiting C++
Started thread
所以很明显,只有工作线程完成其工作后,GUI线程才能打印“Started thread”
知道如何克服这个线程问题吗?
由于
答案 0 :(得分:0)
“看起来好像有一些隐藏的锁”好吧它没有被隐藏得很好,它被称为全局解释器锁(GIL),任何在Python中进行多线程处理的人都应该知道它。 https://wiki.python.org/moin/GlobalInterpreterLock
C ++代码可以获取并释放锁本身。请参阅http://docs.python.org/2/c-api/init.html。
答案 1 :(得分:0)
在从Python调用的扩展代码中执行长时间运行的计算时,您需要release the GIL。
您可以使用配对的宏Py_BEGIN_ALLOW_THREADS
Py_BEGIN_ALLOW_THREADS
,或使用范围经理(例如from here):
class ScopedGILRelease {
public:
inline ScopedGILRelease() { m_thread_state = PyEval_SaveThread(); }
inline ~ScopedGILRelease() {
PyEval_RestoreThread(m_thread_state);
m_thread_state = NULL;
}
private:
PyThreadState* m_thread_state;
};