从 QThread.run 调用 cython 函数时 pyqt gui 被锁定

pyqt gui locked when calling cython function from QThread.run

在 PyQt4 中,我想在没有锁定 gui 的情况下执行代码。 我使用 QThread 并为 gui 更新发出信号。 但是如果我在 QThread.run 中调用 cython 函数,gui 将被锁定。

class Runner(QtCore.QThread):
    iter = QtCore.pyqtSignal(int)

    def __init__(self):
        QtCore.QThread.__init__(self)

    def do_something(self):
        beg = time.time()
        s = 0.0
        while time.time() - beg < 1:
            s += math.sqrt(1000)

    def run(self):
        for it in xrange(10):
            # no lock gui
            #self.do_something()

            # lock gui
            cython_unit.do_something()

            self.iter.emit(it + 1)

cython_unit.pyx:

import math
import time
def do_something():
    beg = time.time()
    s = 0.0
    while time.time() - beg < 1:
        s += math.sqrt(1000)

测试项目在这里:https://github.com/ombschervister/test

由于 GIL(全局解释器锁),一次只能执行一个 Python 实例。 Python 如何选择在线程之间共享时间有点取决于 Python 的版本,但它主要取决于已执行的 Python 字节码的数量(请参阅 https://docs.python.org/3.0/library/sys.html#sys.setcheckinterval)。

我猜,由于 Cython 实际上 运行 没有任何 Python 字节码,因此它永远不会释放 GIL,因此您的显示将被锁定。您可以使用 with nogil 手动释放 GIL。在最坏的情况下,我认为添加:

with nogil:
  pass

你的循环会起作用。但是,您的某些代码绝对可以在没有 GIL 的情况下完成(即不使用 python 功能):

from libc.math cimport sqrt # don't use Python math module
import time

def do_something():
   beg = time.time()
   cdef double s = 0 # typed as a double, not a python object
   while time.time() - beg < 1:
      with nogil:
        s += sqrt(1000) # now using C sqrt, not the python one

如果需要,您也可以使用 libc 时间函数并将几乎整个函数包装在一个 with nogil 块中,在这种情况下,它根本不应该阻塞 GUI。