当前位置: 首页 > 面试题库 >

PyQt5-QThread:线程仍在运行时被销毁

南门正业
2023-03-14
问题内容

我试图弄清楚为什么一旦尝试在线程完成后再次运行线程,此代码就会崩溃。

我第一次单击“开始5个线程”,它运行正常并完成。但是,如果我再次单击它。整个程序崩溃,我得到QThread:线程仍在运行时被销毁错误

可以在网上找到此代码。我正在尝试从中学习。

import time
import sys

from PyQt5.QtCore import QObject, QThread, pyqtSignal, pyqtSlot
from PyQt5.QtWidgets import QApplication, QPushButton, QTextEdit, QVBoxLayout, QWidget


def trap_exc_during_debug(*args):
    # when app raises uncaught exception, print info
    print(args)


# install exception hook: without this, uncaught exception would cause application to exit
sys.excepthook = trap_exc_during_debug


class Worker(QObject):
    """
    Must derive from QObject in order to emit signals, connect slots to other signals, and operate in a QThread.
    """

    sig_step = pyqtSignal(int, str)  # worker id, step description: emitted every step through work() loop
    sig_done = pyqtSignal(int)  # worker id: emitted at end of work()
    sig_msg = pyqtSignal(str)  # message to be shown to user

    def __init__(self, id: int):
        super().__init__()
        self.__id = id
        self.__abort = False

    @pyqtSlot()
    def work(self):
        """
        Pretend this worker method does work that takes a long time. During this time, the thread's
        event loop is blocked, except if the application's processEvents() is called: this gives every
        thread (incl. main) a chance to process events, which in this sample means processing signals
        received from GUI (such as abort).
        """
        thread_name = QThread.currentThread().objectName()
        thread_id = int(QThread.currentThreadId())  # cast to int() is necessary
        self.sig_msg.emit('Running worker #{} from thread "{}" (#{})'.format(self.__id, thread_name, thread_id))

        for step in range(100):
            time.sleep(0.1)
            self.sig_step.emit(self.__id, 'step ' + str(step))

            # check if we need to abort the loop; need to process events to receive signals;
            app.processEvents()  # this could cause change to self.__abort
            if self.__abort:
                # note that "step" value will not necessarily be same for every thread
                self.sig_msg.emit('Worker #{} aborting work at step {}'.format(self.__id, step))
                break

        self.sig_done.emit(self.__id)

    def abort(self):
        self.sig_msg.emit('Worker #{} notified to abort'.format(self.__id))
        self.__abort = True


class MyWidget(QWidget):
    NUM_THREADS = 5

    # sig_start = pyqtSignal()  # needed only due to PyCharm debugger bug (!)
    sig_abort_workers = pyqtSignal()

    def __init__(self):
        super().__init__()

        self.setWindowTitle("Thread Example")
        form_layout = QVBoxLayout()
        self.setLayout(form_layout)
        self.resize(400, 800)

        self.button_start_threads = QPushButton()
        self.button_start_threads.clicked.connect(self.start_threads)
        self.button_start_threads.setText("Start {} threads".format(self.NUM_THREADS))
        form_layout.addWidget(self.button_start_threads)

        self.button_stop_threads = QPushButton()
        self.button_stop_threads.clicked.connect(self.abort_workers)
        self.button_stop_threads.setText("Stop threads")
        self.button_stop_threads.setDisabled(True)
        form_layout.addWidget(self.button_stop_threads)

        self.log = QTextEdit()
        form_layout.addWidget(self.log)

        self.progress = QTextEdit()
        form_layout.addWidget(self.progress)

        QThread.currentThread().setObjectName('main')  # threads can be named, useful for log output
        self.__workers_done = None
        self.__threads = None

    def start_threads(self):
        self.log.append('starting {} threads'.format(self.NUM_THREADS))
        self.button_start_threads.setDisabled(True)
        self.button_stop_threads.setEnabled(True)

        self.__workers_done = 0
        self.__threads = []
        for idx in range(self.NUM_THREADS):
            worker = Worker(idx)
            thread = QThread()
            thread.setObjectName('thread_' + str(idx))
            self.__threads.append((thread, worker))  # need to store worker too otherwise will be gc'd
            worker.moveToThread(thread)

            # get progress messages from worker:
            worker.sig_step.connect(self.on_worker_step)
            worker.sig_done.connect(self.on_worker_done)
            worker.sig_msg.connect(self.log.append)

            # control worker:
            self.sig_abort_workers.connect(worker.abort)

            # get read to start worker:
            # self.sig_start.connect(worker.work)  # needed due to PyCharm debugger bug (!); comment out next line
            thread.started.connect(worker.work)
            thread.start()  # this will emit 'started' and start thread's event loop

        # self.sig_start.emit()  # needed due to PyCharm debugger bug (!)

    @pyqtSlot(int, str)
    def on_worker_step(self, worker_id: int, data: str):
        self.log.append('Worker #{}: {}'.format(worker_id, data))
        self.progress.append('{}: {}'.format(worker_id, data))

    @pyqtSlot(int)
    def on_worker_done(self, worker_id):
        self.log.append('worker #{} done'.format(worker_id))
        self.progress.append('-- Worker {} DONE'.format(worker_id))
        self.__workers_done += 1
        if self.__workers_done == self.NUM_THREADS:
            self.log.append('No more workers active')
            self.button_start_threads.setEnabled(True)
            self.button_stop_threads.setDisabled(True)
            # self.__threads = None

    @pyqtSlot()
    def abort_workers(self):
        self.sig_abort_workers.emit()
        self.log.append('Asking each worker to abort')
        for thread, worker in self.__threads:  # note nice unpacking by Python, avoids indexing
            thread.quit()  # this will quit **as soon as thread event loop unblocks**
            thread.wait()  # <- so you need to wait for it to *actually* quit

        # even though threads have exited, there may still be messages on the main thread's
        # queue (messages that threads emitted before the abort):
        self.log.append('All threads exited')


if __name__ == "__main__":
    app = QApplication([])

    form = MyWidget()
    form.show()

    sys.exit(app.exec_())

问题答案:

通过让他作为父母自己来解决这个问题。您必须更改:

thread = QThread()

至:

thread = QThread(parent=self)


 类似资料:
  • 问题内容: 我有一个批处理文件,可在Windows 2003服务器中启动Java进程。根据安全策略,如果用户在特定时间段内处于非活动状态,则该计算机的用户将被强制注销。问题在于,当用户注销时,该过程也会终止。 我安排了一个新任务(“控制面板”->“计划任务”),并选择了“计算机启动时”选项,并在那里提供了用户帐户详细信息。但这似乎没有任何效果,用户仍然注销并且该进程终止。是否需要重新启动才能使此更

  • 本文向大家介绍PyQt 线程类 QThread使用详解,包括了PyQt 线程类 QThread使用详解的使用技巧和注意事项,需要的朋友参考一下 PyQt中的线程类 QtCore.QThread ,使用时继承QThread类 启动界面的线程暂称为UI线程。界面执行命令时都在自己的UI线程中。 如果在UI线程中执行网络连接和数据库操作等耗时的操作,界面会被卡住,Windows下有可能会出现“无响应”的

  • 问题内容: 我正在尝试使用lib做一个邮箱检查器,它在没有gui的python,队列和多线程环境下可以正常工作。 但是,当我尝试放置一个gui时,我所做的每个功能都将gui冻结直到完成。 我尝试了各种文档中的许多东西(添加qthread,信号,游标等),而教程对我没有用。 有人可以帮助我了解如何在运行函数时设置文本或将文本附加到QtextEdit吗,因为它仅在完成后才起作用。 这是我的代码: 问题

  • 问题内容: 我有一个Web应用程序,每小时与中央数据库同步四次。该过程通常需要2分钟。我想将此进程作为线程在X:55,X:10,X:25和X:40运行,以便用户知道在X:00,X:15,X:30和X:45他们有数据库的干净副本。这只是管理期望。我已经通过了执行器,但是调度是通过它完成的,我认为不能保证以小时为单位的实际运行时间。我可以使用第一个延迟来启动,以便第一个接近启动时间和每15分钟的计划,

  • 我的线程基本上是一些动作的循环。 我在循环中设置了一些检查,基本上检查布尔值是否仍然为真。布尔值在启动服务时设置为true,在停止服务时设置为false。更具体地说,我在我的服务类的ondestory()方法中将布尔值设置为false。 示例: 问题在于,当系统停止服务时,不会调用方法。那么,当服务不再运行时,如何正确地停止线程?