[英]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
信号与错误线程相关联的某种信号/插槽问题。
注意,在工作完成之前,我不会遇到该exit
或quit
块。 我只是在经历线程根本不起作用。 即主窗口被阻止,我什至不能单击按钮甚至发起退出线程
问题在于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.