python-3.x - 无法正确停止 QThread 线程(PySide2)
问题描述
我无法正确关闭 QThread 线程。线程最后应该运行一个非常基本的网络服务器,使用 gevent 和烧瓶。我将我的问题简化为以下运行的最小示例。不包括 webserver 部分,仅启动和停止线程。
代码概述:
- 我的两个类都继承自 QtCore.QObject
- 我使用“moveToThread()”而不是子类化 QThread
- 按下 CTRL-C 会从主应用程序向工作人员发出信号
- 网络服务器关闭后(未在最小代码中显示),工作程序向主应用程序发出信号
- 主应用程序应该等待(超时 5 秒)线程完成并退出。
问题:
- 主应用程序在5s后超时,但它应该先停止线程
发现:
- 似乎发出了两个信号(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
主文件
#!/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()
推荐阅读
- android - 如何将 Firebase Firestore 与 Fragments 一起使用?
- android - 许多 GeoJSON 或 shapefile 点作为 Android 中的地理围栏?
- python - 对于语句,SQLalchemy 行数始终为 -1
- java - 从给定的单链表中反转偶数(仅)
- javascript - 如何在打字稿中动态合并两个数组对象?
- arduino - ESP8266 连接到 wifi,但没有注册它
- facebook - Facebook 服务器到服务器的事件中的用户数据
- r - 行上的相关性
- java - Wildfly 不需要的 EJB 注入
- php - 如何使用具有不同节点的php循环遍历xml