首页 > 解决方案 > 无法正确停止 QThread 线程(PySide2)

问题描述

我无法正确关闭 QThread 线程。线程最后应该运行一个非常基本的网络服务器,使用 gevent 和烧瓶。我将我的问题简化为以下运行的最小示例。不包括 webserver 部分,仅启动和停止线程。

代码概述:

问题:

发现:

输出:

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

主文件

#!/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…&quot;) 

       # 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()

标签: python-3.xpyside2qthread

解决方案


这是一种对我有用的解决方案,可能还有其他解决方案。我用<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()  

推荐阅读