我无法正确关闭QThread线程。线程应该在最后运行一个非常基本的Web服务器,使用gevent和flask。我将我的问题简化为下面的运行最小示例。不包括Web服务器部分,只包括启动和停止线程。
代码概述:
- 我的两个类都继承自QtCore.QObject
- 我使用";moveToThread(("而不是对QThread进行子分类
- 按下CTRL-C会从主应用程序向工作人员发出信号
- 在Web服务器关闭后(未在最小代码中显示(,工作程序向主应用程序发出一个信号
- 主应用程序应该等待线程完成(超时5s(,然后退出
问题:
- 主应用程序在5s后超时,但它应该先停止线程
结果:
- Boths信号(main->worker,worker->main(似乎会发出并激活插槽
- 但是,我打印报表的时间戳显示,来自worker的信号->main只有在超时5秒后才会激活main中的插槽。我预计它会提前5秒(见底部第二行的"输出"(
输出:
19:31:58.438 Main: started with ID <PySide2.QtCore.QThread(0x55b69412f880) at 0x7f817459a748>
19:31:58.441 T_CTRL: Constructor
19:31:58.441 ThreadWorker: Constructor
19:31:58.442 T_CTRL: Thread started
19:31:58.446 ThreadWorker: Slot activated startServer()
19:31:58.447 ThreadWorker: Thread ID <PySide2.QtCore.QThread(0x55b69412bc80) at 0x7f817459a808>
19:32:12.296 T_CTRL: Sigint handler starting exit process…
19:32:12.297 ThreadWorker: Slot activated requestServerStop()
19:32:12.297 ThreadWorker: Signal emitted 'sigServerShutdownFinished'
19:32:17.299 T_CTRL: Slot activated from signal sigServerShutdownFinished <- problem ?!
19:32:17.299 T_CTRL: Sigint handler - thread didn’t stop, wait timed out
Main.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import sys
from PySide2.QtCore import QCoreApplication, QTimer, QThread
from T_CTRL import T_CTRL
from datetime import datetime
def main():
# create application
app = QCoreApplication(sys.argv)
app.aboutToQuit.connect(app.deleteLater)
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " Main: started with ID " + str(QThread.currentThread()))
# create my control object
myCtrl = T_CTRL()
# # create helper timer, so that quit signals can be received
timerHelper = QTimer()
timerHelper.start(500)
timerHelper.timeout.connect(lambda: None) # Let the interpreter run each 500 ms.
# run application and create main event handler
return app.exec_()
if __name__ == '__main__':
main()
T_CTRL.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PySide2 import QtCore
from PySide2.QtCore import QThread, QCoreApplication, Qt
from T_Web_Worker import ThreadWorker
import signal
from functools import partial
from datetime import datetime
class T_CTRL(QtCore.QObject):
# signals
sigShutdown = QtCore.Signal()
# objects
t = None
def __init__(self):
# init
super(T_CTRL, self).__init__()
# Init
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Constructor")
# https://mayaposch.wordpress.com/2011/11/01/how-to-really-truly-use-qthreads-the-full-explanation/
# create thread objects
self.t = QThread()
self.worker = ThreadWorker()
# connect quit signals to sigint_handler
signal.signal(signal.SIGINT, partial(self.sigint_handler, str1=""))
signal.signal(signal.SIGTERM, partial(self.sigint_handler, str1=""))
# move worker to thread
self.worker.moveToThread(self.t)
# connect signals/slots to start the thread
self.t.started.connect(self.worker.startServer)
# connect signals/slots for the shutdown of thread and webserver
self.worker.sigServerShutdownFinished.connect(self.t.quit)
self.worker.sigServerShutdownFinished.connect(self.worker.deleteLater)
self.worker.sigServerShutdownFinished.connect(self.printSlotsigServerShutdownFinished)
self.t.finished.connect(self.t.deleteLater)
self.t.finished.connect(self.printTfinished, Qt.QueuedConnection)
# start the thread
self.t.start()
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Thread started")
# connect signals/slots for the shutdown of the webserver
self.sigShutdown.connect(self.worker.requestServerStop)
# @QtCore.Slot()
def printSlotsigServerShutdownFinished(self):
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Slot activated from signal sigServerShutdownFinished")
# @QtCore.Slot()
def printTfinished(self):
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: thread sent finished signal")
def __del__(self):
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Destructor")
# -------------------------------------------------------------------------
# sigint_handler
# -------------------------------------------------------------------------
def sigint_handler(self, signal, frame, str1):
# entry
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Sigint handler starting exit process…")
# send Shtudownsignals -> 1) server shutdown 2) thread shutdown
self.sigShutdown.emit()
QCoreApplication.processEvents()
# waiting for the server and thread to shutdown
ret = self.t.wait(5000) # wait for thread to close, but max 5000ms
QCoreApplication.processEvents()
if ret:
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Sigint handler - thread stopped")
else:
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " T_CTRL: Sigint handler - thread didn’t stop, wait timed out")
# everything has been shut down, now quit the applicaiton
QCoreApplication.quit()
T_Web_Worker.py
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from PySide2 import QtCore
from PySide2.QtCore import QCoreApplication, QThread
from datetime import datetime
class ThreadWorker(QtCore.QObject):
# signal to feedback, that worker has finished
sigServerShutdownFinished = QtCore.Signal()
def __init__(self):
super(ThreadWorker, self).__init__()
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " ThreadWorker: Constructor")
def __del__(self):
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " ThreadWorker: Destructor")
# Start the wworker
def startServer(self):
#print ("worker 1: " + datetime.utcnow().strftime('%H:%M:%S.%f')[:-3])
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " ThreadWorker: Slot activated startServer()")
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " ThreadWorker: Thread ID " + str(QThread.currentThread()))
QCoreApplication.processEvents()
# shutdown requested by main thread
@QtCore.Slot()
def requestServerStop(self):
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " ThreadWorker: Slot activated requestServerStop()")
QCoreApplication.processEvents()
self.sigServerShutdownFinished.emit()
print (datetime.utcnow().strftime('%H:%M:%S.%f')[:-3] + " ThreadWorker: Signal emitted 'sigServerShutdownFinished'")
QCoreApplication.processEvents()
这是一个适合我的解决方案,可能还有其他解决方案。我用另一个插槽替换了<QThread>.wait()
命令。我认为wait()
正在阻塞我的事件循环。
与我最初的问题相比,相关的变化如下:
T_CTRL.py
def __init__(self):
...
# connect: webserver told that is has shut down, now stop the thread
self.worker.sigServerShutdownFinished.connect(self.t.quit)
self.worker.sigServerShutdownFinished.connect(self.worker.deleteLater)
...
# connect: thread has finished, now quit the application
self.t.finished.connect(self.t.deleteLater)
self.t.finished.connect(self.quit_now)
# emit signal to Worker
def sigint_handler(self, signal, frame, str1):
self.sigShutdown.emit()
# REMOVED THE FOLLOWING LINE and replaced it by the slot quit_now()
# ret = self.t.wait(5000)
...
# slot activated when the thread emitted its finished signal
@QtCore.Slot()
def quit_now(self):
QCoreApplication.quit()