一般简单的业务场景不需要创建子线程,但是当有复杂的业务逻辑时,主线程(UI线程)需要等待这个业务处理完毕,例如,在一个单线程中产生一千万一个随机数,这时再拖动鼠标点击窗口会发生卡顿,影响用户体验。需要注意的是,只有主线程才能操作UI窗口,当子线程需要向窗口某个控件传递数据时,只能将数据传递给主线程(信号和槽),再由主线程操作窗口。Qt中的创建子线程有两种方式。
方法一:
1、创建一个线程子类,让其继承于QThread类
class MyThread:public Qthread { ... }
2、在我们写的线程类中重写父类的run()方法
class MyThread:public Qthread { ... void run() override; ... } MyThread::void run() { //这是子线程的业务逻辑 }
3、在UI线程中创建子线程对象,在某个契机启动子线程run方法。
... MyThread *mythread = new MyThread; ... connect(ui->pushbutton,&QPushButton::clicked,this,[=](){ mythread->start(); });
一个demo,一个线程产生随机数,一个线程将这些随机排序。再由主线程将这两组数据显示在主窗口中控件中。
mythread.h
class GenRandom : public QThread { Q_OBJECT public: explicit GenRandom(QObject *parent = nullptr); void run() override; public slots: void getnum(int); //槽函数接受主线程发送来num signals: void sendvector(QVector<int>); //将随机数发送给主线程 private: int m_num; //由主线程告诉子线程产生多少个随机数 QVector<int> m_v; };
mythread.cpp
void GenRandom::getnum(int n) { m_num=n; } //重写,生成随机数 void GenRandom::run() { qDebug()<<"线程号:"<<Generate::currentThreadId(); for(int i=0;i<m_num;i++) { m_v.push_back(rand()%100); } emit sendvector(m_v); //将数据发送给ui线程 }
ui线程:widget的构造函数
Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { GenRandom *random = new GenRandom; //告诉子线程产生随机数的个数 connect(this,&Widget::sendnum,gen,&GenRandom::getnum); //启动子线程 connect(ui->pushButton,&QPushButton::clicked,[=](){ emit sendnum(1000); random->start(); }); //子线程将1000个数传递过来,主线程显示这1000个随机数 connect(random,&GenRandom::sendvector,this,[=](QVector<int> list){ for(int i=0;i<list.size();i++) { ui->listWidget_random->addItem(QString::number(list.at(i))); } }); }
排序线程写在mythread文件中。
.h
class Qsort:public QThread { Q_OBJECT public: explicit Qsort(QObject *parent=nullptr); void run() override; protected: void quicksort(QVector<int> &m_v,int low,int high); int partiton(QVector<int> &m_v,int low,int high); public slots: void getarray(QVector<int> list); //获取随机数 signals: void finishsort(QVector<int>); //排序完成后发送给ui private: QVector<int> m_v; };
.cpp
void Qsort::run() { qDebug()<<"快速排序的线程:"<<Qsort::currentThreadId(); quicksort(m_v,0,m_v.size()-1); emit finishsort(m_v); } void Qsort::quicksort(QVector<int>& m_v, int low, int high) { if(low<high) { int pos=partiton(m_v,low,high); quicksort(m_v,low,pos-1); quicksort(m_v,pos+1,high); } } int Qsort::partiton(QVector<int> &m_v, int low, int high) { int pivot = m_v[low]; while (low<high) { while(low<high&&m_v[high]>=pivot) --high; m_v[low]=m_v[high]; while (low<high&&m_v[low]<=pivot) ++low; m_v[high]=m_v[low]; } m_v[low]=pivot; return low; } void Qsort::getarray(QVector<int> list) { m_v=list; }
ui线程:widget的构造函数
//创建对象 Qsort *q=new Qsort; //接受随机线程发送来随机数据 connect(random,&GenRandom::sendvector,q,&Qsort::getarray); //开启线程 connect(random,&GenRandom::sendvector,this,[=](QVector<int> list){ q->start(); }); //接受排序线程发送来的有序数据 connect(q,&Qsort::finishsort,this,[=](QVector<int> list) { for(int i=0;i<list.size();i++) { ui->listWidget_qsort->addItem(QString::number(list.at(i))); } });
方法二:
1、创建一个新的类,这个类从QObject派生
class newthread:public QObject { ... }
2、在newthread类中添加公共的自定义函数,用来处理子线程逻辑,这个自定义函数可以有参数
class newthread:public QObject { public: void mythread1(。); }
3、在UI线程创建QThread子线程对象
QThread *thread = new QThread;
4、在UI线程创建任务类,不能指定父对象
newthread *worker = new newthread;
5、将worker移动到子线程对象中
worker->movetoThread(thread);
6、启动子线程,但是此时任务类中自定义函数并没有工作,通过信号和槽的方式,启动工作函数。
以上面产生随机数中线程为例
//当收到主线程发送来的信号才真正开始 connect(this,&MainWindow::starting,gen,&GenRandom::work_Gen); connect(ui->pushButton,&QPushButton::clicked,[=](){ emit starting(1000); thread1->start(); });