前言
以前我多线程使用传统的继承qthread重写run()或者继承qrunable类把对象丢到线程池解决。经过昨天的面试让我了解到新的技术,我之前看到过只不过没有详细的去了解movetotread技术,这个技术是qt5推出的,qt6还在延续使用
代码结构
以下是简单的movetotread的实现
class Worker:public QObject{
Q_OBJECT
private slots:
dowork();/*业务逻辑*/
}//使用方式
QThread thread;
Worker woker;
woker.movetotread(&tread);
thread.start();
//通过信号触发work
QObject::connect(&sender,&sender::startsignal,&worker,&worker::doworke);
实现逻辑
movetotread实现了线程与事务相分离,但需要运行某个事件的时候只需要将继承了QObject类的对象通过movetotread(&目标线程)传进去执行,通过qdequeueconnect()实现跨线程通讯。
关键操作
初次创建线程的时候通过start()函数,它会调用run()函数并启动事件循环(默认exec())
退出线程有三种
thread->quit();//优雅退出
tread->wait();//主线程阻塞等待子线程结束
thread->terminate();//强制退出线程,不推荐这种,可能导致线程未释放
创建好线程以后可以写两个connect管理对象和线程当线程执行完时释放finished信号来管理对象和线程的生命周期
connect(thread,&qthread::finished,worker,&QObject::deletelater);
connect(thread,&qthread::finished,thread,&QObject::deletelater);
跨线程的时候不要直接调用对象内的方法,可以通过主线程connect信号触发worker->doworker();
其他注意细节
如果线程未被释放,是可以通过movetothread将新的对象传进去。
如果向一个线程里面重复movetothread,最新的对象会把上一个对象给替代掉。
小实验
我设计两个线程,一个是主线程,里面是mainwindow,一个是子线程,专门生产1-6的随机数,生产好了以后将生成的数字传递到主线程显示出来
首先是建一个随机数生成器类randomnum
//头文件
#ifndef RANDOMNUM_H
#define RANDOMNUM_H
#include<QObject>
#include<QRandomGenerator>
#include<QTimer>class randomnum : public QObject
{Q_OBJECT
public:explicit randomnum(QObject *parent = nullptr);
public slots:void startrandom();//开始生成随机数void stoprandom();//暂停生成
signals:void newnumber(int count,int number);
private:QTimer *timer;//计时器int count=0;//计数QRandomGenerator random;//随机数生成器
};#endif // RANDOMNUM_H
//cpp
#include "randomnum.h"
#include "qobject.h"randomnum::randomnum(QObject *parent): QObject{parent}
{timer=new QTimer(this);connect(timer,&QTimer::timeout,this,[this](){count++;int num=random.bounded(1,6);emit newnumber(count,num);});
}void randomnum::startrandom()
{timer->start(1000);
}void randomnum::stoprandom()
{timer->stop();
}
主程序
//头文件
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:MainWindow(QWidget *parent = nullptr);~MainWindow();private:Ui::MainWindow *ui;QThread *thread1;private slots:void appendnum(int count,int num);};
#endif // MAINWINDOW_H
//cpp文件
#include "randomnum.h"
#include "ui_mainwindow.h"
#include<QString>MainWindow::MainWindow(QWidget *parent): QMainWindow(parent), ui(new Ui::MainWindow)
{ui->setupUi(this);ui->plainTextEdit->setReadOnly(true);//创建工作对象和线程randomnum *random1=new randomnum;thread1=new QThread(this);//把工作对象移动到子线程random1->moveToThread(thread1);//连接信号槽//触发开始随机数的信号//connect(thread1,&QThread::started,random1,&randomnum::startrandom);//把子线程生成的信号参数传给主线程对应的槽函数connect(random1,&randomnum::newnumber,this,&MainWindow::appendnum);//开始按钮connect(ui->btnstart,&QPushButton::clicked,random1,&randomnum::startrandom);//暂停按钮connect(ui->btnstop,&QPushButton::clicked,random1,&randomnum::stoprandom);//管理对象生命周期connect(thread1,&QThread::finished,random1,&QObject::deleteLater);//connect(thread1,&QThread::finished,thread1,&QObject::deleteLater);thread1->start();
}MainWindow::~MainWindow()
{if(thread1&&thread1->isRunning()){thread1->quit();thread1->wait();}delete ui;
}void MainWindow::appendnum(int count, int num)
{QString str=QString("第%1次,数字是%2").arg(count).arg(num);ui->plainTextEdit->appendPlainText(str);
}
显示效果
程序怎么实现的看上面代码就行了,这里讲下遇到的问题:
1.关闭程序,程序崩溃,子线程没有停止导致的,刚开始的时候明明有一个connect连接finished的信号,结果还是没关掉,原因是deletlater只是标记了他为待删除,不需要这一句,直接在析构函数里面显示删除,之前那一句不要了,避免重复删除
2.添加开始/停止按钮,用这个直接操作计时器,不是操控线程就可以实现