繁体   English   中英

为什么我的QThread所做的工作使主线程饿了?

[英]Why is the work being done by my QThread starving the main thread?

这是我基于QThread编写的可爱线程。 您会注意到它有一个事件队列。 4秒后,将触发一个事件并在doWork中进行一些工作。 doWork应该在所有打印之间休眠,并给其他线程运行的机会。 可以说,所有的doWork打印和睡眠时间都足够长,以至于另一个线程确实应该花一些时间来执行。

from PySide.QtCore import *
from PySide.QtGui import *

class DoStuffPeriodically(QThread):
    def __init__(self):
        super(DoStuffPeriodically, self).__init__()

    def doWork(self):
        #... do work, post signal to consumer
        print "Start work"
        for i in range(0,100):
            print "work %i" % i
            QThread.msleep(10)
        print "Done work"
        return

    def run(self):
        """ Setup "pullFiles" to be called once a second"""
        self.timer= QTimer()
        self.timer.setSingleShot(True)
        self.timer.timeout.connect(self.doWork)
        self.timer.start(4000)
        self.exec_()

这是我用来控制线程的顶级QT小部件。 它基本上只是一个启动/停止线程的按钮。

class Widg(QWidget):
    def __init__(self):
        super(Widg, self).__init__()
        self.thread = DoStuffPeriodically()
        self.startStopButton = QPushButton()
        hBoxLayout = QHBoxLayout()
        hBoxLayout.addWidget(self.startStopButton)
        self.startStopButton.pressed.connect(self.startStopThread)
        self.setLayout(hBoxLayout)
        self.threadRunning = False

    def startStopThread(self):
        if self.threadRunning:
            print "Stopping..."
            self.thread.exit(0)
            self.threadRunning = False
            print "Stopped"
        else:
            print "Starting..."
            self.thread.start()
            self.threadRunning = True
            print "Started"


if __name__ == "__main__":
    from sys import argv
    qApp = QApplication(argv)    
    widg = Widg()
    widg.show()
    qApp.exec_()

如果单击startStopButton,我希望看到线程开始打印

Starting...
Started...
Start Work
work 0
work 1
...
work 99
Done Work

但是我要做的是能够在执行工作时停止线程。 我期待一些类似的东西

Starting...
Started...
Start Work
work 0
work 1
...
work N
Stopping...
work 99
Done Work
Stopped...

相反, 工作线程似乎正在阻止主线程执行? 我必须等待工作完成后才能单击startStopButton,这给了我

Starting...
Started...
Start Work
work 0
work 1
...
work 99
Done Work
Stopping...
Stopped...

doWork运行多长时间都没有关系。 我已经将其循环了10000次。 它似乎从来没有花时间返回主线程,并且小部件没有响应。 我是否正在做一些阻止实际线程实际工作的事情?

(我正在使用python 2.7和pyside 1.10。)

更新资料

如果我修改run以直接执行工作,而不是基于QTimer则线程似乎可以正常工作。 即更改run为:

def run(self):
    self.doWork()
    return

这不能解决我的问题,因为我想使用事件队列运行。 因此,我怀疑这是QTimer信号与错误线程相关联的某种信号/插槽问题。

注意,在工作完成之前,我不会遇到该exitquit块。 我只是在经历线程根本不起作用。 即主窗口被阻止,我什至不能单击按钮甚至发起退出线程

问题在于QThread方法正在完成工作。 QThread的线程相似性始终是创建 QThread的线程 因此,该信号告诉QThread的所属线程执行doWork -在这种情况下为主线程。 因此,即使在此QThread中定义了doWork ,该工作仍由主线程完成。 我知道那种心神扭曲。 为了解释,让我首先引用文档

QThread对象位于另一个线程中,即创建该线程的那个线程。

因此,当此信号/插槽连接已建立时

 self.timer.timeout.connect(self.doWork)

它,默认情况下是一个AutoConnection

(默认值)如果信号是从与接收对象不同的线程发出的,则该信号将排队,表现为Qt :: QueuedConnection。 否则,将以Qt :: DirectConnection的形式直接调用该插槽。 连接类型是在发出信号时确定的。

信号的源是我的QThread,因为QTimer是在run方法中创建的,但目标是主线程。 它正在主线程的事件队列中排队! 解决方案是创建第二个工作线程QObject,它具有当前线程的亲和力:

class Worker(QObject):
    def __init__(self, parent):
        super(Worker, self).__init__(parent=parent)

    def doWork(self):
        #... do work, post signal to consumer
        print "Start work"
        for i in range(0,1000):
            print "work %i" % i
            QThread.msleep(100)
        print "Done work"
        return

然后运行成为:

def run(self):
    """ Setup "pullFiles" to be called once a second"""
    print "Running..."       
    self.worker = Worker(parent=None) #affinity = this thread
    self.timer= QTimer() #affinity = this thread
    print self.timer.thread()
    self.timer.setSingleShot(True)
    self.timer.timeout.connect(self.worker.doWork)
    self.timer.start(4000)
    self.exec_()
    print "Exec_ done"

这可行。 信号的源和目的地都在一个线程中,并且不会遍历到主线程。 瞧!

QThread :: exit()文档中:

Tells the thread's event loop to exit with a return code.

您的doWork是事件循环中的单个事件。 事件循环称为您的事件,因此等待它完成。 exit是另一个事件,它已排入事件循环并等待doWork完成。 msleep有助于提高GUI的响应速度(使它有时间重新绘制和执行按钮处理程序),但实际上并不能使exit事件以某种方式潜行。

如果您希望doWork随时可中断,则必须更改逻辑。 使计时器更频繁地触发并且仅增加一。 然后退出即可在此之间的任何时间行动。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM