MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include //包含该文件,使用信号和槽
#include //添加头文件
class MyThread : public QThread //注意修改基类,原先是继承QObject,改成 QThread!!!
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0); //会自动生成
protected:
//run() 是继承下来的虚函数
// 是 protected 的函数
//也是唯一的线程处理函数,注意是唯一
//不能直接调用,正确姿势是通过start()间接调用
void run();
signals:
void isDone(); //线程运行后发出结束信号,只声明不实现
public slots:
};
#endif // MYTHREAD_H
MyThread.cpp
#include "MyThread.h"
MyThread::MyThread(QObject *parent) : QThread(parent) //记得修改基类!!!
{
}
void MyThread::run(){
//在这里执行很复杂的数据处理
//假设,很复杂的数据处理需要耗时5s
sleep(5); //这是Qt的睡觉函数,以秒为单位 ,加 m 是毫秒,加 u 是微秒,C++的睡觉 S 要大写
emit isDone(); //最后一刻再释放信号
}
实现的功能是:点击按钮,计时器开始计时,子线程开始运行复杂的操作,再次点击,子线程结束,计时器停止计时:
Widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include //线程头文件
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void dealTimeout(); //定时器槽函数,定时器开始计时后要做些什么
void dealDone(); //子线程结束槽函数,子线程结束后,要做些什么
void stopThread(); //停止线程槽函数
private slots:
void on_pushButton_clicked(); //窗口按钮的点击事件
private:
Ui::Widget *ui;
QTimer * myTimer; //声明定时器,可以不适用指针,但是推荐使用
MyThread *thread; //声明子线程,可以不适用指针,但是推荐使用
};
#endif // WIDGET_H
Widget.cpp
#include "Widget.h"
#include "ui_Widget.h"
#include
#include
#include //该写的都写上
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//实例化定时器,一定要在构造时候就实例化
myTimer = new QTimer(this);
//只要定时器启动。自动触发 timeout(),定时器关闭后则不执行
connect(myTimer,&QTimer::timeout , this , &Widget::dealTimeout);
//分配空间
thread = new MyThread(this);
//关联结束动作
connect(thread, &MyThread::isDone, this, &Widget::dealDone);
//关闭窗口右上角时,触发
connect(this,&Widget::destroyed,this,&Widget::stopThread);
}
Widget::~Widget()
{
delete ui;
}
void Widget::dealTimeout(){
static int i =0;
i++;
//设置lcd的值
ui->lcdNumber->display(i); //读者在窗口放一个 LCDNumber 控件,用于显示计时器走了多长时间
}
void Widget::dealDone(){
qDebug()<< "it is over";
myTimer->stop(); //关闭定时器,这里的 stop() 是 QTimer 自己的方法
}
void Widget::stopThread(){
//thread->terminate(); //坚决不要用,涉及到动态分配内存就会出现内存泄漏问题
thread->quit(); //这个会等到数据处理完,才退出
thread->wait(); //等待结束(会出现阻塞,但是感觉不到),回收资源
}
void Widget::on_pushButton_clicked()
{
//如果定时器没有工作,这个判断语句是良好的代码规范
if(myTimer->isActive() == false){
myTimer->start(100); //只执行100毫秒
}
//启动线程,处理数据
//start() 间接调用 run() 函数
thread->start();
}
main.cpp
#include "Widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w; //实例化窗口
w.show(); //展示出来
return a.exec();
}
至此子线程的创建与停止就实现了,效果见下图:
使用继承 QThread 的注意事项:
1、修改 MyThread.h 和 MyThread.cpp 的继承方式(修改为 QObject)
2、run() 函数是唯一的处理复杂操作的函数,MyThread定义的其他函数仍在主线程执行
3、注意子线程的信号返回时机
4、调用线程的窗口在构造时就要实例化一个 MyThread
5、没了
添加 C++ 类,注意选择:
MyThread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = 0);
//没有继承 QThread,所以没有run() 函数
//自定义线程处理函数,自己命名
//同样的,也是唯一的线程处理函数,其他函数仍运行在主线程
void myTimeout();
//设置线程开始与结束的标记位,在后续会有用途讲解
void setFlag(bool flag = true);
signals:
void mySignal(); //这个是子线程运行结束后发出的信号
public slots:
private:
//标志位,在后面会有讲解
bool isStop;
};
#endif // MYTHREAD_H
MyThread.cpp
#include "MyThread.h"
#include
#include
#include
MyThread::MyThread(QObject *parent) : QObject(parent)
{
//线程一开始是要执行的,所以设置为 false ,注意逻辑
isStop = false;
}
void MyThread::myTimeout(){
while(isStop == false){ //如归线程在运行,才继续执行
//在这里写复杂的操作
QThread::sleep(1); //这里是秒
emit mySignal(); //每次执行都发送信号
//线程函数内部绝对不允许操作图形界面,会报错
//QMessageBox::aboutQt(NULL);
qDebug()<<"子线程号 : "<<QThread::currentThread();
if(true == isStop){ //如果主线程将isstop设置为true,则子线程停止执行,调出while循环
break;
}
}
}
void MyThread::setFlag(bool flag){
isStop = flag;
}
Widget.h
在窗口放置两个按钮,一个start,一个stop,然后还是放置一个 LCDNumber
#ifndef WIDGET_H
#define WIDGET_H
#include
#include //主要是为了使用它的函数
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
void dealSignal(); //处理来自子线程的信号
void dealDestroy(); //处理关闭窗口事件,主要用来结束线程
private slots:
void on_pushButton_clicked(); //start按钮的操作 --- 开始线程
void on_pushButton_2_clicked(); //stop 按钮的操作 --- 结束线程
signals:
void startThread(); //启动子线程的信号
private:
Ui::Widget *ui; //UI 界面
//注意以下两者的区别
MyThread *myT; //线程对象
QThread *thread; //子线程
};
#endif // WIDGET_H
MyThread.cpp
#include "Widget.h"
#include "ui_Widget.h"
#include
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//myT 分配空间,一定不能指定父对象!!!
myT = new MyThread;
//myT = new MyThread(this);
//创建子线程
thread = new QThread(this);
//把我自定义的线程加入到子线程中
//如果 myT 指定了父对象,那么的话,就是说系统不允许你把有父亲的儿子移动到别的父亲
myT->moveToThread(thread); //关键操作
//运行中的子线程发出了信号,UI线程接收信号(用于更新界面UI,这里是更新的lcdNumber)
connect(myT,&MyThread::mySignal,this,&Widget::dealSignal);
//UI线程发出启动子线程信号,子线程接收到信号后开始执行复杂运算
connect(this,&Widget::startThread,myT,&MyThread::myTimeout); //第五个参数的使用 Qt::QueuedConnection(默认) 和 Qt::DirectConnection
//UI线程发出的关闭窗口信号,UI线程做子线程善后工作
connect(this,&Widget::destroyed,this,&Widget::dealDestroy);
//打印UI线程句柄
qDebug()<<"主线程号 : "<<QThread::currentThread();
}
Widget::~Widget()
{
delete ui;
}
void Widget::dealSignal(){ //改变UI界面
static int i=0;
i++;
ui->lcdNumber->display(i);
}
void Widget::dealDestroy(){
on_pushButton_2_clicked();
delete myT; //不是必须的,但是不写可能会导致内存泄漏
}
void Widget::on_pushButton_clicked()
{
//良好的代码写法
if(thread->isRunning() == true)
return;
//启动线程,但是没有启动线程处理函数
thread->start();
myT->setFlag(false);
//不能直接调用线程处理函数,直接调用导致线程处理函数和主线程在同一个线程
//myT->myTimeout();
//只能通过 signal - slot 调用
emit startThread(); //这才是正确执行线程函数的姿势
}
void Widget::on_pushButton_2_clicked()
{
if(thread->isRunning() == false)
return;
myT->setFlag(true); //这种做法实际上停止不了线程,但是也要写,日后会知道好处的
thread->quit(); //这样才是正确姿势
thread->wait();
}
main.cpp
#include "Widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
看看执行效果吧:
使用继承 QObject 的注意事项:
1、不用 MyThread.h 和 MyThread.cpp 的继承方式,因为它本身就继承 QObject
2、不需要实现 run() 函数,但是自己得写一个运行复杂操作的函数,并且它也是唯一的处理复杂操作的函数,MyThread定义的其他函数仍在主线程执行
3、注意子线程的信号返回时机
4、调用线程的窗口在构造时就要实例化一个 QThread * 和 MyThread*
5、涉及到 主线程释放信号 - 子线程处理 和 子线程释放信号 - 主线程处理 的信号 - 槽机制
· 复杂的操作一定要开辟多线程,完美解决复杂操作主界面卡死的情况,亦即我我们经常见到的程序未响应;
· 作为一名学生党,搞科研,处理“大数据”,多线程必不可少
· Qt 的多线程还是很方便的;
· 加油吧,站在鄙人失败的肩膀上继续向前~~~