【问题标题】:pyqt QThread blocking main threadpyqt QThread 阻塞主线程
【发布时间】:2013-12-03 05:33:05
【问题描述】:

我正在尝试创建一个简单的线程应用程序,其中我有一个执行一些长时间处理的方法和一个显示加载栏和取消按钮的小部件。

我的问题是,无论我如何实现线程,它实际上都没有线程 - 一旦线程启动,UI 就会被锁定。我已经阅读了所有教程并发布了关于此的内容,我现在正在求助于询问社区来尝试解决我的问题,因为我不知所措!

最初我尝试继承 QThread 直到互联网说这是错误的。然后我尝试了 moveToThread 方法,但它产生了零差异。

初始化代码:

loadingThreadObject = LoadThread(arg1)
loadingThread = PythonThread()
loadingThreadObject.moveToThread(loadingThread)
loadingThread.started.connect(loadingThreadObject.load)
loadingThread.start()

PythonThread 类(显然 QThreads 在 pyQt 中存在错误,除非你这样做,否则不会启动):

class PythonThread (QtCore.QThread):
    def __init__(self, parent=None):
        QtCore.QThread.__init__(self, parent)

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

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

LoadThread 类:

class LoadThread (QtCore.QObject):
    results = QtCore.Signal(tuple)

    def __init__ (self, arg):
         # Init QObject
         super(QtCore.QObject, self).__init__()

         # Store the argument
         self.arg = arg

    def load (self):
         #
         # Some heavy lifting is done
         #

         loaded = True
         errors = []

         # Emits the results
         self.results.emit((loaded, errors))

非常感谢任何帮助!

谢谢。 本。

【问题讨论】:

  • “繁重”的本质是什么?这个问题的答案可能会影响线程是否会提供任何好处(因为 python 的 GIL 施加的限制)。
  • 主要涉及SQL查询。不过,我对响应式 UI 的性能优势并不感兴趣。
  • 我不是在谈论性能。如果 GIL 在长时间运行的任务期间没有释放,那么,本身,线程不会阻止阻塞。您需要将任务分成块并定期向主 GUI 线程发送信号,以便它可以处理任何未决事件(即调用 qApp.processEvents() 或其他东西)。
  • 很公平,那么运行 SQL 查询是否会锁定 GIL?如果是这样,我有点搞砸了,因为查询可能需要几分钟才能运行......
  • 我对 SQL 知之甚少,但似乎 GIL 尽可能发布。不过,我想这可能取决于您使用的特定 SQL 库。

标签: python multithreading pyqt


【解决方案1】:

问题出在我使用的 SQL 库(一个自定义的内部解决方案)上,结果证明它不是线程安全的,因此会执行阻塞查询。

如果您遇到类似问题,请先尝试删除 SQL 调用并查看它是否仍然阻塞。如果这解决了阻塞问题,请尝试通过MySQLdb(或您正在使用的数据库类型的等效项)使用原始 SQL 重新引入您的查询。这将诊断问题是否与您选择的 SQL 库有关。

【讨论】:

    【解决方案2】:

    连接到started 信号的函数将运行它所连接的线程,即主 GUI 线程。但是,QThread 的start() 函数在线程初始化后在线程中执行其run() 方法,因此应创建QThread 的子类,其运行方法应运行LoadThread.load,即您要执行的函数。不要从 PythonThread 继承,没有必要。应该使用 QThread 子类的start() 方法来启动线程。

    PS:由于在这种情况下QThread的run()方法的子类只调用LoadThread.load(),所以run()方法可以简单地设置为LoadThread.load

    class MyThread(QtCore.QThread):
        run = LoadThread.load # x = y in the class block sets the class's x variable to y
    

    一个例子:

    import time
    from PyQt4 import QtCore, QtGui
    import sys
    application = QtGui.QApplication(sys.argv)
    
    class LoadThread (QtCore.QObject):
        results = QtCore.pyqtSignal(tuple)
    
        def __init__ (self, arg):
             # Init QObject
             super(QtCore.QObject, self).__init__()
    
             # Store the argument
             self.arg = arg
    
        def load(self):
             #
             # Some heavy lifting is done
             #
             time.sleep(5)
             loaded = True
             errors = []
    
             # Emits the results
             self.results.emit((loaded, errors))
    
    l = LoadThread("test")
    
    class MyThread(QtCore.QThread):
        run = l.load
    
    thread = MyThread()
    
    button = QtGui.QPushButton("Do 5 virtual push-ups")
    button.clicked.connect(thread.start)
    button.show()
    l.results.connect(lambda:button.setText("Phew! Push ups done"))
    application.exec_()
    

    【讨论】:

    • 我也有类似的问题,你找到解决办法了吗?
    • 嗨@user2145312,是的 - 结果我使用的 SQL 库(一个自定义的内部解决方案)不是线程安全的,因此执行了阻塞查询。如果您的问题也与 SQL 有关,请首先尝试删除 SQL 调用并查看它是否仍然阻塞。如果这解决了阻塞问题,请尝试通过MySQLdb(或您正在使用的数据库类型的等效项)使用原始 SQL 重新引入您的查询。这将诊断问题是否与您选择的 SQL 库有关。
    猜你喜欢
    • 1970-01-01
    • 2017-07-31
    • 2017-02-13
    • 2012-02-20
    • 2018-02-16
    • 1970-01-01
    • 2015-12-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多