Advertisement

python销毁线程_PyQt5 QThread:线程仍在运行时被销毁

阅读量:

我试图弄清楚为什么这些代码在线程完成后再次运行它们时会崩溃。在

我第一次点击“开始5线程”,它运行良好,结束。但如果我再按一下。整个程序崩溃了,我得到了QThread:Destroyed,而线程仍在运行错误

这个代码是在网上找到的。我正在努力从中学习。在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_())

全部评论 (0)

还没有任何评论哟~