- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在构建一个 Qt GUI 应用程序,它使用 QThread/QObject 组合充当在主线程之外执行操作的工作程序。
通过moveToThread
,QObject 被移动到QThread 中。这样,我的工作人员就可以拥有在事件循环(由 QThread 提供)中处理的信号(它是 QObject)和槽。
现在我想让 worker 以一种特殊的方式运行,只要事件循环中的槽遇到 Python 异常,它们就会优雅地停止线程。
通过一些测试,我发现在 PyQt5 中,槽中的一个异常导致整个应用程序停止,据我所知,与 PyQt4 相比,这是一个有意的改变,在 PyQt4 中只打印了异常,但事件循环保持不变运行。我读到可以通过将您自己的“excepthook”猴子修补到 sys.excepthook
来避免这种情况,Qt 以停止解释器的方式实现它。
所以我做到了,到目前为止这很有效。此外,excepthook 使我能够在发生异常时 exit()
我的工作人员,对此我在其他地方找不到更好的方法。我尝试子类化 QThread 并在 QThread 的 run()
方法中围绕对 exec_()
的调用放置一个 try..except
,但它没有'传播事件循环中发生的异常...所以剩下的唯一选择是将 try..except
block 放在每个插槽中,我想避免这种情况。还是我错过了什么?
下面是一个 MWE,它展示了我目前所拥有的。我的问题是当异常发生时退出线程不会立即发生,用 error
槽演示,这导致调用 thread.exit()
除了钩子(Hook)。相反,线程事件循环中的所有其他剩余事件都将被执行,这里由我安排在它后面的 do_work
插槽演示。 exit()
似乎只是将另一个事件安排到队列中,一旦它被处理,事件循环就会停止。
我该如何解决这个问题?有没有办法刷新 QThread
的事件队列?我能以某种方式优先退出吗?
或者也许是另一种完全不同的方式来捕获槽中的异常并使线程停止,而不停止主程序?
代码:
import sys
import time
from qtpy import QtWidgets, QtCore
class ThreadedWorkerBase(QtCore.QObject):
def __init__(self):
super().__init__()
self.thread = QtCore.QThread(self)
self.thread.setTerminationEnabled(False)
self.moveToThread(self.thread)
self.thread.start()
def schedule(self, slot, delay=0):
""" Shortcut to QTimer's singleShot. delay is in seconds. """
QtCore.QTimer.singleShot(int(delay * 1000), slot)
class Worker(ThreadedWorkerBase):
test_signal = QtCore.Signal(str) # just for demo
def do_work(self):
print("starting to work")
for i in range(10):
print("working:", i)
time.sleep(0.2)
def error(self):
print("Throwing error")
raise Exception("This is an Exception which should stop the worker thread's event loop.")
# set excepthook to explicitly exit Worker thread after Exception
sys._excepthook = sys.excepthook
def excepthook(type, value, traceback):
sys._excepthook(type, value, traceback)
thread = QtCore.QThread.currentThread()
if isinstance(thread.parent(), ThreadedWorkerBase):
print("This is a Worker thread. Exiting...")
thread.exit()
sys.excepthook = excepthook
# create demo app which schedules some tasks
app = QtWidgets.QApplication([])
worker = Worker()
worker.schedule(worker.do_work)
worker.schedule(worker.error) # this should exit the thread => no more scheduling
worker.schedule(worker.do_work)
worker.thread.wait() # worker should exit, just wait...
输出:
starting to work
working: 0
working: 1
working: 2
working: 3
working: 4
working: 5
working: 6
working: 7
working: 8
working: 9
Throwing error
Traceback (most recent call last):
File "qt_test_so.py", line 31, in error
raise Exception("This is an Exception which should stop the worker thread's event loop.")
Exception: This is an Exception which should stop the worker thread's event loop.
This is a Worker thread. Exiting...
starting to work
working: 0
working: 1
working: 2
working: 3
working: 4
working: 5
working: 6
working: 7
working: 8
working: 9
期望:
输出应该在“Exiting...”之后结束。
最佳答案
QThread.exit
的 Qt 文档有点误导:
Tells the thread's event loop to exit with a return code.
After calling this function, the thread leaves the event loop and returns from the call to QEventLoop::exec(). The QEventLoop::exec() function returns returnCode.
By convention, a returnCode of 0 means success, any non-zero value indicates an error.
Note that unlike the C library function of the same name, this function does return to the caller -- it is event processing that stops. [emphasis added]
这表明在调用 exit()
之后,不会再对线程的事件队列进行进一步处理。但事实并非如此,因为 QEventLoop
总是调用 processEvents
before 检查它是否应该退出。这意味着当 exec()
返回时,事件队列将始终为空。
在您的示例中,单次计时器会将事件发布到接收线程的事件队列,最终将调用连接的插槽。因此,无论您做什么,所有这些插槽都将在线程最终退出之前被调用。
解决这个问题的一个相当简单的方法是使用 requestInterruption
具有装饰器的功能,用于检查是否应调用插槽:
def interruptable(slot):
def wrapper(self, *args, **kwargs):
if not self.thread.isInterruptionRequested():
slot(self, *args, **kwargs)
return wrapper
class Worker(ThreadedWorkerBase):
test_signal = QtCore.pyqtSignal(str) # just for demo
@interruptable
def do_work(self):
print("starting to work")
for i in range(10):
print("working:", i)
time.sleep(0.2)
@interruptable
def error(self):
print("Throwing error")
raise Exception("This is an Exception which should stop the worker thread's event loop.")
def excepthook(type, value, traceback):
sys.__excepthook__(type, value, traceback)
thread = QtCore.QThread.currentThread()
if isinstance(thread.parent(), ThreadedWorkerBase):
print("This is a Worker thread. Exiting...")
thread.requestInterruption()
thread.exit()
sys.excepthook = excepthook
关于multithreading - 在 QThread.exit() 上立即停止处理事件队列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50179666/
我遇到一种情况,我需要从某个主题读取(正在进行的)消息并将它们放入另一个 Queue 中。我怀疑我是否需要 jms Queue 或者我可以对内存中的 java Queue 感到满意。我将通过同一 jv
队列的定义 队列(Queue):先进先出的线性表 队列是仅在队尾进行插入和队头进行删除操作的线性表 队头(front):线性表的表头端,即可删除端 队尾(rear):线性表的表尾端,即可插入端 由于这
Redis专题-队列 首先,想一想 Redis 适合做消息队列吗? 1、消息队列的消息存取需求是什么?redis中的解决方案是什么? 无非就是下面这几点: 0、数据可以顺序读
0. 学习目标 栈和队列是在程序设计中常见的数据类型,从数据结构的角度来讲,栈和队列也是线性表,是操作受限的线性表,它们的基本操作是线性表操作的子集,但从数据类型的角度来讲,它们与线性表又有着巨大的不
我想在 redis + Flask 和 Python 中实现一个队列。我已经用 RQ 实现了这样的查询,如果你有 Flask 应用程序和任务在同一台服务器上工作,它就可以正常工作。我想知道是否有可能创
我正在使用 Laravel 5.1,我有一个大约需要 2 分钟来处理的任务,这个任务特别是生成报告...... 现在,很明显,我不能让用户在我接受用户输入的同一页面上等待 2 分钟,而是我应该在后台处
我正在使用 Azure 队列,并且有多个不同的进程从队列中读取数据。 我的系统的构建方式假设每条消息只读取一次。 这个Microsoft article声称 Azure 队列具有至少一次传送保证,这可
我正在创建一个Thread::Queue元素数组。 我这样做是这样的: for (my $i=0; $i new; } 但是,当我在每个队列中填充这样的元素时 $queues[$index]->enq
我试图了解如何将我的 Mercurial 补丁推送到远程存储库(例如 bitbucket.org),而不必先应用它们(实际上提交它们)。我的动机是在最终完成之前首先对我的工作进行远程备份,并且能够与其
我的本地计算机上有一个 Mercurial 队列补丁,我需要与同事共享该补丁,但我不想将其提交到上游存储库。有没有一种简单的方法可以打包该补丁并与他分享? 最佳答案 mq 将补丁作为不带扩展名的文
Java 中是否有任何类提供与 Queue 相同的功能,但有返回对象的选项,并且不要删除它,只需将其设置在集合末尾? 最佳答案 Queue不直接提供这样的方法。但是,您可以使用 poll 和 add
我在Windows上使用Tortoise svn客户端,我需要能够一次提交来自不同子文件夹的更改文件-一次提交。像在提交之前将文件添加到队列中之类的?我该怎么做? Windows上是否还有另一个svn
好吧,我正在尝试对我的 DSAQueue 类进行单元测试,它显示我的 isEmpty()、isFull() 和 dequeue() 方法失败。 以下是我的 DSAQueue 代码。我认为我的 Dequ
我想尽量减少对传入请求的数据库查询。它目前需要写入 6 个不同的表。在返回响应之前不需要完成处理。因此,我考虑了 laravel 队列,但我想知道我是否也可以摆脱写入队列/作业表所需的单独查询。我可以
我正在学习队列数据结构。我想用链表创建队列。我想编程输出:10 20程序输出:队列为空-1 队列为空-1 我哪里出错了? 代码如下: class Node { int x; Node next
“当工作人员有空时,他们会根据主题的优先级列表从等待请求池中进行选择。在时间 t 到达的所有请求都可以在时间 t 进行分配。如果两名工作人员同时有空,则安排优先权分配给最近的工作最早安排的人。如果仍然
我正在开发一个巨大的应用程序,它使用一些子菜单、模式窗口、提示等。 现在,我想知道在此类应用程序中处理 Esc 和单击外部事件的正确方法。 $(document).keyup(function(e)
所以 如果我有一个队列 a --> b --> NULL; 当我使用函数时 void duplicate(QueueNodePtr pHead, QueueNodePtr *pTail) 它会给 a
我正在尝试为键盘输入实现 FIFO 队列,但似乎无法让它工作。我可以让键盘输入显示在液晶显示屏上,但这就是我能做的。我认为代码应该读取键盘输入并将其插入队列,然后弹出键盘输入并将值读取到液晶屏幕上。有
我正在学习算法和 DS。如何在 JavaScript 中使用队列? 我知道你可以做这样的事情。 var stack = []; stack.push(2); // stack is now
我是一名优秀的程序员,十分优秀!