无法正确停止QThread线程(PySide2)



我无法正确关闭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()  

最新更新