QThread异常处理和线程竞争

3

我有一个使用QThread的GUI(PySide)应用程序。当异常发生时,我的QThread中会发出一个信号,以便我可以在主线程中处理异常。但是,启动线程的其余函数仍然会执行。我尝试使用wait函数来阻止执行,但它不起作用。这是我的实现:

QThread子类:

class LongTaskThread(QtCore.QThread):
    task_finished = QtCore.Signal()
    task_failed = QtCore.Signal(Exception)

    def __init__(self, allow_log=True, test_mode=False, parent=None):
        QtCore.QThread.__init__(self, parent)

    def run(self):
        self.task_failed.emit(Exception())

    def wait_with_gui_refresh(self):
        while self.isRunning():
            time.sleep(0.1)
            if not self.test_mode:
                QtGui.QApplication.processEvents()

主线程

def test():
   my_thread = LongTaskThread()
   my_thread.task_finished.connect(on_finished)
   my_thread.task_failed.connect(on_failed)
   my_thread.start()
   # my_thread.wait()    <---- tentative 1
   # my_thread.wait_with_gui_refresh()    <---- tentative 2
   print('bla bla bla bla')

def on_finished)():
   pass

def on_failed(err):
  raise err

我原本以为print语句永远不会执行,但无论是使用wait函数、wait_with_gui_refresh函数还是什么都不用,在QThread中抛出异常时,print语句总是被执行。

如何在QThread内部抛出异常时停止测试函数?


wait() 只是等待执行完成,它不关心是因为成功完成还是由于异常而结束。 - 8bitwide
好的,关于wait(),在print调用和异常处理之间存在线程竞争吗?那wait_with_gui_refresh呢?它会将发送到GUI的信号出队,然后异常应该在打印之前被触发。 - GuillaumeA
1个回答

4
在你的test函数中,事件发生的顺序如下:
  1. 线程开始
  2. 调用线程的run方法
  3. task_failed信号异步发出(即它被发布到接收者的事件队列中)
  4. 线程的run方法返回
  5. 如果在这里调用线程的wait方法,它将立即返回True,因为没有需要等待的内容(即run已经返回)
  6. 打印一条消息,并且test返回
  7. 控制返回到事件循环,并且处理task_failed信号
  8. on_failed中引发异常
看起来这里没有什么可以反对的地方。显然,您不想在工作线程运行时阻塞gui,因此异步处理任何异常是非常有道理的。但是为了实现这一点,控制必须返回到主线程的事件循环-这意味着test函数必须立即返回。如果您想在线程开始后运行一些代码,请将一个槽连接到其started信号。

1
谢谢,我同意你的观点。这个线程在这里是为了更新进度条,使得gui必须响应。正如你所发现的那样,这更多是一个设计问题而不是多线程问题。打印调用应该在on_finished处理函数中完成,而不是在启动线程后。 - GuillaumeA

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接