0

我创建了一个简单的 GUI 应用程序,并希望在工作线程而不是事件调度线程上运行长时间的操作,以保持 GUI 响应。

只要工作线程正在运行 python 函数,它就可以正常工作。但是,如果函数在 C++ 代码上执行某些操作,则 GUI 线程会阻塞,就好像在它而不是在工作线程上执行了延长的操作一样!

看起来好像有一些隐藏的锁阻止 GUI 线程与工作线程并行运行。

我怀疑它可能与正在使用的 UI 框架有关,所以我尝试了 TKinter 和 wxPython 并且两者都出现了问题。

我在 Windows 7 上使用 Visual Studio 2010 和 python 2.7.5

这是 C++ 代码:

注意:我也尝试过使用 Sleep(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()

单击“Busy Python”按钮时,可以看到 GUI 响应(该按钮看起来未单击)并且这些打印输出按此顺序显示,其中“已启动线程”立即出现,正如预期的那样:

Started thread
Done waiting python

单击“Busy C++”按钮时,可以看到 GUI 没有响应(按钮在等待时看起来像被单击),并且这些打印输出按此顺序显示,其中两者仅在等待结束时出现:

Done waiting C++
Started thread

所以很明显,只有在工作线程完成工作后,GUI 线程才能打印“已启动线程”

知道如何克服这个线程问题吗?

谢谢

4

2 回答 2

0

"看起来好像有一些隐藏的锁" 嗯,它并没有隐藏得那么好,它被称为全局解释器锁 (GIL),任何在 Python 中进行多线程处理的人都应该知道它。https://wiki.python.org/moin/GlobalInterpreterLock

C++ 代码可以自己获取和释放锁。请参阅http://docs.python.org/2/c-api/init.html

于 2013-11-12T09:03:44.680 回答
0

在从 Python 调用的扩展代码中执行长时间运行的计算时,您需要释放 GIL 。

您可以使用配对的宏Py_BEGIN_ALLOW_THREADS Py_BEGIN_ALLOW_THREADS,或使用范围管理器(例如,从这里):

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;
};
于 2013-11-12T09:09:07.143 回答