Qt多线程的实现方式之继承QThread重写run函数
一、一些概念1.QThread本身不在子线程;2.QThread对象本身通常是通过主线程创建的3.只有run()里面才真正跑在新线程4.QThread类里写的普通成员函数、槽函数默认都在主线程执行。二、线程亲和性Thread Affinity)1.每个QObject的一生都会归属于某一个线程这个归属关系就叫线程亲和性;2.线程亲和性决定了一个QObject对象的所有 异步行为 在哪个线程执行;3.异步行为1)信号 → 槽的触发;2)posted event 自定义事件3)QTimer定时器超时;4)网络/串口数据到达;5)deleteLater()延迟删除6)Qt内部各种事件。三、线程的生命周期创建对象 → 初始化 → 启动线程 → 运行任务/事件循环) → 退出 → 结束 → 销毁1.创建QThread对象只是创建了控制器对象此时还没有操作系统线程对象所在线程 创建它的线程通常是主线程)QThread *thread new QThread; //动态创建 QTread thread; //静态创建2.调用start()真正创建系统线程1)向操作系统申请创建线程;2)操作系统分配线程资源、ID、栈空间;3)操作系统调度开始执行 run();4)发出信号started()。3.进入run()线程真正开始void run() override { // 子线程从这里开始 exec(); //开启事件循环(可选) }1)不调用exec()run跑完线程直接结束;2)调用exec()线程进入事件循环可以持续接收信号/事件。4.运行中状态可通过isRunning()成员判断在此状态可处理自定义业务逻辑、事件循环信号槽、定时器、网络事件)。5.线程退出1)正常退出thread-quit(); // 通知事件循环退出 //或 thread-exit(); 仅对带事件循环exec()的线程有效退出事件循环 → run() 执行完毕 → 线程结束。2)暴力终止thread-terminate(); 操作系统强制杀死线程不释放资源、不清理栈、不执行析构。6.线程结束1)run()执行完毕;2)发出信号finished();3)操作系统回收线程栈、ID 等资源4)此时线程已不存在但QThread对象还在。7.销毁QThread对象thread-deleteLater(); //或 thread-wait(); delete thread;四、测试MyThread.h#ifndef MYTHREAD_H #define MYTHREAD_H #include QObject #include QThread #include QDebug class MyThread : public QThread { Q_OBJECT public: MyThread(QThread *parent nullptr): QThread(parent) { qDebug() MyThread() QThread::currentThreadId(); m_stop false; } ~MyThread() { qDebug() ~MyThread() QThread::currentThreadId(); } //对外提供停止函数 void stop() { m_stop true; } protected: //重新实现父类的run函数 void run() override { #if 1 //不使用事件循环 while(!m_stop) { QThread::sleep(1); qDebug() run() 所在线程ID: QThread::currentThreadId(); } qDebug() run() 结束; #endif #if 0 //使用事件循环 qDebug() run() 所在线程ID: QThread::currentThreadId(); exec(); // 开启事件循环 qDebug() exec() 退出run() 结束; #endif } private: // 头文件加原子标志 std::atomicbool m_stop; }; #endif // MYTHREAD_HWorker.h#ifndef WORKER_H #define WORKER_H #include QObject #include QThread #include QDebug class Worker : public QObject { Q_OBJECT public: explicit Worker(QObject *parent nullptr) : QObject(parent) { qDebug() Worker() QThread::currentThreadId(); } ~Worker() { qDebug() ~Worker() QThread::currentThreadId(); } public slots: void doWork() { while(1) { QThread::sleep(3); // 打印当前线程 ID qDebug() doWork 所在线程ID: QThread::currentThreadId(); qDebug() doWork 所属线程: this-thread(); } } }; #endif // WORKER_HWidget.h#ifndef WIDGET_H #define WIDGET_H #include QWidget class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent nullptr); ~Widget(); public slots: signals: //信号让worker干活 void sigStartWork(); }; #endif // WIDGET_HWidget.cpp#include Widget.h #include MyThread.h #include Worker.h #include QThread #include QDebug Widget::Widget(QWidget *parent) : QWidget(parent) { qDebug() 主线程ID: QThread::currentThreadId(); qDebug() this-thread(); MyThread *thread new MyThread(); Worker *worker new Worker; qDebug() 没调用moveToThread之前的所属线程: worker-thread(); //把worker丢进子线程 worker-moveToThread(thread); //重写的run()函数中没有调用exec()此句代码无意义 //连接信号与槽() connect(this, Widget::sigStartWork, worker, Worker::doWork);//重写的run()函数中没有调用exec()此句代码无意义 //连接信号与槽() connect(thread, QThread::started, [](){ qDebug() 线程 started; }); /*保证线程退出后能正常调用worker、thread的析构函数*/ connect(thread, QThread::finished, worker, QObject::deleteLater); //重写的run()函数中没有调用exec()此句代码无意义 connect(thread, QThread::finished, thread, QThread::deleteLater); //重写的run()函数中没有调用exec()此句代码无意义 //开启线程调用run() thread-start(); //发信号让worker干活 emit sigStartWork(); //重写的run()函数中没有调用exec()此句代码无意义 #if 0 //调用exec() //线程退出正常析构 //thread-quit(); ////重写的run()函数中没有调用exec()此句代码无意义 #endif #if 1 //没有调用exec() QThread::sleep(3); thread-stop(); //自定义标记用于线程退出正常析构 #endif } Widget::~Widget() { }main.cpp#include Widget.h #include QApplication int main(int argc, char *argv[]) { QApplication a(argc, argv); Widget w; w.show(); return a.exec(); }五、结论1.没有exec(),没有事件循环1)不能接收信号槽;2)不能用QTimer;3)不能处理网络/串口事件;4)不能用deleteLater ();5)所有异步行为都无效。它是死循环阻塞式线程, 必须加标志退出线程适合周期性采集数据、后台轮询、不需要交互、不需要信号的业务场景。没有事件循环,即使把对象move过来信号也无法投递,槽永远不会执行。2.exec()(事件循环)1)有事件循环;2)能接收信号;3)能交互、能控制4)exec大概处理流程while (!quit) { // 1.看有没有消息/信号/事件 if (有事件) { 处理事件(调用槽、定时器、网络回调……) } else { // 没消息就挂起把CPU让给别人 休眠等待; } } 它是事件循环线程适合复杂任务。六、开发工具及Qt版本开发工具:Qt Creator 4.10.2Qt版本:Qt_5_12_6