继承QThread实现多线程,是一种错误的做法,具体见:
The main thing to keep in mind when using a QThread is that it’s not a thread. It’s a wrapper around a thread object. This wrapper provides the signals, slots and methods to easily use the thread object within a Qt project. This should immediately show why the recommended way of using QThreads in the documentation, namely to sub-class it and implement your own run() function, is very wrong. A QThread should be used much like a regular thread instance: prepare an object (QObject) class with all your desired functionality in it. Then create a new QThread instance, push the QObject onto it using moveToThread(QThread*) of the QObject instance and call start() on the QThread instance. That’s all. You set up the proper signal/slot connections to make it quit properly and such, and that’s all.
意就是说:不要把QThread当成一个thread,它仅仅是用来把object包装为thread对象的包装器而已。这个包装器提供了信号,槽等各种易使用的方法。官方文档中提到的继承QThead类,然后重写run方法,是一种错误的方式。正确方式是:创建一个QObject对象,这个对象包含了你想调用的函数,然后创建一个QThread实例,把QObject对象通过moveToThread()方法放到线程里,通过调用QThread的start()方法来启动QObject的方法,从而实现线程操作。
QtCore.QThread是一个管理线程的类,当我们使用其构造函数的时候,便新建了一个线程。这里要强调,QThread是一个线程管理器,不要把业务逻辑放在这个类里面,Qt的作者已经多次批评继承QThread类来实现业务逻辑的做法。正确的姿势应该是:
self.worker_thread = QtCore.QThread()
self..moveToThread(self.worker_thread)
self.worker_thread.start()
therad定义成局部变量,会发生主进程结束而子线程仍未结束的错误
worker_thread = QtCore.QThread()
解决方法是将thread定义为类变量
self.worker_thread = QtCore.QThread()
将业务逻辑写在一个继承了QtCore.QObject的子类里面,然后新建一个实例。
例如上述代码的woker,然后调用继承了父类的方法moveToThread方法,把该对象放进线程里面。剩下的工作就是通过信号和槽的机制处理业务逻辑和返回结果了。一般来说,槽函数所在的类在哪个线程,这个函数就在哪个线程执行。
在类成员变量里定义signal:
foo_signal = QtCore.pyqtSignal(int)
stop_signal = QtCore.pyqtSignal()
from PyQt5 import QtCore
class FooObject(QtCore.QObject):
# signal 要在__init__方法之前定义
foo_signal = QtCore.pyqtSignal(int)
stop_signal = QtCore.pyqtSignal()
def __init__(self):
super(FooObject, self).__init__()
@QtCore.pyqtSlot()
def run(self):
counter = 0
for _ in range(10):
for i in range(5):
self.foo_signal.emit(counter) # 发射信号,参数将被槽函数接收
counter += 1
QtCore.QThread.sleep(1)
if __name__ == "__main__":
import sys
app = QtCore.QCoreApplication(sys.argv)
foo = FooObject()
thread = QtCore.QThread()
thread.start() # 开启任务线程
foo.moveToThread(thread)
foo.foo_signal.connect(print) # 连接槽函数
QtCore.QTimer.singleShot(1000, foo.run)
sys.exit(app.exec_())
- 建立信号与槽的连接
foo.foo_signal.connect(print) # 这里连接的是Python内置的print()方法
- 启动线程
thread.start()
- 发射信号
self.foo_signal.emit(counter)
需要注意:仅当同步任务结束或子线程中有睡眠(sleep)时槽函数才会被调用
- 结束线程的方法
if self.thread.isRunning():
self._thread.quit()
# self._thread.terminate()# 强制结束
线程锁(QMutex)
在业务逻辑的方法中加入线程锁
qmut = QMutex() # 创建线程锁
class FooObject(QObject): # 线程1
def __init__(self):
super().__init__()
def run(self):
qmut.lock() # 加锁
values = [1, 2, 3, 4, 5]
for i in values:
print(i)
time.sleep(0.5) # 休眠
qmut.unlock() # 解锁
PyQt中正确的多线程使用方式不是继承QThread,而应创建QObject对象包含业务逻辑,然后将其移动到QThread中。避免在QThread中处理业务,确保线程作为类变量,使用信号槽机制协调执行。QMutex线程锁用于同步任务和防止数据竞争。

1万+

被折叠的 条评论
为什么被折叠?



