50、继承方式创建QThread---------多线程

张开发
2026/4/7 23:08:46 15 分钟阅读

分享文章

50、继承方式创建QThread---------多线程
继承方式创建QThread这种方法通过继承 QThread 类并重写其 run() 方法来实现线程逻辑。run() 方法中可以包含需要在线程中执行的代码。我们看一下QThread的构造函数explicit QThread(QObject *parent nullptr);所以子类继承QThread子类构造也写成显示构造并且参数为QObject*即可。QThread有一个run函数我们需要在子类中重写virtual void run();线程启动函数为, 不传递参数就是默认启动[slot] void QThread::start(QThread::Priority priority InheritPriority)传递参数 就是设置优先级, QT提供了几种设置优先级的方式以下是QThread::Priority枚举的常量QThread::IdlePriority: 表示空闲优先级。线程将仅在系统空闲时运行。QThread::LowestPriority: 表示最低优先级。线程的优先级低于正常优先级。QThread::LowPriority: 表示低优先级。线程的优先级低于正常优先级但高于最低优先级。QThread::NormalPriority: 表示正常优先级。线程的默认优先级。QThread::HighPriority: 表示高优先级。线程的优先级高于正常优先级。QThread::HighestPriority: 表示最高优先级。线程的优先级高于所有其他优先级。QThread::TimeCriticalPriority: 表示时间关键优先级。此优先级用于需要及时处理的线程。QThread::InheritPriority: 表示继承优先级。线程将继承其父线程的优先级。线程启动后是在后台运行的如果主线程退出也就代表了主进程退出此时子线程也会被回收所以有时候需要在主线程中等待子线程执行完主线程再退出。等待子线程退出需要调用如下函数bool wait(unsigned long time ULONG_MAX)wait函数如果不传递参数表示无限制的等待直到线程运行完成否则传递的参数表示最多等待指定毫秒时间特别注意因为我们自己定义的线程类需要发送信号根据前面学过的元对象机制我们需要在自己定义的类里添加Q_OBJECT宏声明。所以我们自定声明的线程类应该是这个样子#include QThread class WorkerThread : public QThread { Q_OBJECT public: //显示构造并且指定父节点 explicit WorkerThread(QObject *parent nullptr); //要重写的run函数 virtual void run() override; };接下来实现构造函数WorkerThread::WorkerThread(QObject* parent):QThread(parent) { }重写线程执行函数void WorkerThread::run() { qDebug() 线程开始 QThread::currentThreadId(); // 执行耗时任务 for(int i 0; i 5; i) { qDebug() 计数 i; sleep(1); // 模拟耗时操作 } qDebug() 线程结束 QThread::currentThreadId(); }重写的函数里我们打印了5个数字每打印1次睡眠1s。并且在开始和结束的时候打印了线程id。然后我们在主函数中启动线程并且等待线程退出#include QApplication #include workerthread.h int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); //等待线程退出 thread.wait(); return a.exec(); }程序输出线程开始 0x9fe0 计数 0 计数 1 计数 2 计数 3 计数 4 线程结束 0x9fe0因为a.exec()阻塞所以主线程没有退出。进阶思考阻塞UI如果我把主函数调用改成启动mainwindow然后在show()函数后调用thread.wait()在线程运行期间界面能响应点击事件吗int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); MainWindow mainwindow; mainwindow.show(); //等待线程退出 thread.wait(); return a.exec(); }答案是子线程运行的时候主界面不能响应点击事件。这段代码运行后会看到鼠标在主界面上转圈圈。直到子线程运行结束后鼠标才恢复正常。因为在线程运行期间主线程会阻塞等待子线程thread.wait返回进而不会走入a.exec()a.exec()是执行事件循环的函数因为子线程运行期间主线程没有执行事件循环所以主线程不会响应ui事件。修改优化我们可以把等待子线程退出放在a.exec()之后即可int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); MainWindow mainwindow; mainwindow.show(); //a.exec()阻塞执行事件轮询 int res a.exec(); //等待线程退出 thread.wait(); return res; }再谈事件轮询a.exec()是执行事件轮询而且是阻塞函数内部执行死循环通过轮询的方式检测每隔QObject对象的事件队列如果有未处理的就处理直到所有的消息都处理完。所以我们可以得出结论只要一个对象继承了QObject并且这个对象有任务或者事件未完成这个exec()就不会返回。进阶思考后台运行不等待如果我启动一个子线程后主线程不等待直接退出会怎样将main函数改为如下int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); //等待任务执行完成退出 return a.exec(); }上面的程序会等到子线程执行完退出因为子线程是继承自QThread的类在子线程有任务未完成时主线程的exec()不会退出。上面的程序输出线程开始 0x9e40 计数 0 计数 1 计数 2 计数 3 计数 4 线程结束 0x9e40好的那么我们接下来不调用a.exec()会怎么样呢代码改成如下int main(int argc, char *argv[]) { QApplication a(argc, argv); //创建线程 WorkerThread thread; //启动线程 thread.start(); //无窗口则直接退出 return 0; }程序运行后会出现如下异常这段报错的意思是主线程退出的时候回收子线程但是子线程仍然在运行。所以程序异常结束了。使用心得所以同学们使用多线程的时候一定要注意线程的生命周期一定要等待子线程运行结束后主线程再退出否则会出现主线程退出强制回收子线程如果子线程未完成任务被强制回收会导致进程异常。

更多文章