Qt 多线程有两种实现方法:
1.继承QThread 重写run()方法:
只有run()里面的函数是在新的线程里,所以要把耗时的操作全部放到run(),这样会有一定的局限性
2.继承QObject moveToThread
Qt4.8之后,Qt官方建议使用第二种方法,这个方法比较灵活,下一篇在介绍
本篇介绍 第一种 继承QThread 重写run()方法
|————————————————————————————————————————————————-|
1.启动一个线程
为了验证只有run() 在新线程里运行,我们还写了一个 work(),在ui界面类里面调用一下,打印 QThreadID 看一下到底是不是在新线程里运行.
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QThread>
class myThread :public QThread
{
Q_OBJECT
public:
myThread(QObject* parent);
void run();//只有run()里面在新的线程里
void work();
signals:
void currentProgress(int value);
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QDebug>
myThread::myThread(QObject *parent)
{
qDebug()<<"construction ThreadID:"<<QThread::currentThreadId();
}
void myThread::run()
{
for(int i = 0;i<=10;i++)
{
qDebug()<<"QThread::run() ThreadID:"<<QThread::currentThreadId();
emit currentProgress(i * 10);
msleep(200);
}
}
void myThread::work()
{
qDebug()<<"do work ThreadID:"<<QThread::currentThreadId();
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "mythread.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void on_startButton_clicked();
void receiveProgress(int value);
void on_doWorkButton_clicked();
private:
Ui::Widget *ui;
myThread* m_thread;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
qDebug()<<"UI widget ThreadId:"<<QThread::currentThreadId();
ui->progressBar->setValue(0);
m_thread = new myThread(this);
connect(m_thread,&myThread::currentProgress,this,&Widget::receiveProgress);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_startButton_clicked()
{
m_thread->start();
}
void Widget::receiveProgress(int value)
{
ui->progressBar->setValue(value);
}
void Widget::on_doWorkButton_clicked()
{
m_thread->work();
}
!!!!!!!!!!!!很明显只有run是在线程里面!!!!!!!!!!
!!!!!!!!!!!!很明显只有run是在线程里面!!!!!!!!!!
2.如果关闭一个线程?
eixt?quit?terminate?
我们还是来验证一下
void Widget::on_exitButton_clicked()
{
m_thread->exit();
qDebug()<<"clicked m_thread exit";
}
void Widget::on_quitButton_clicked()
{
m_thread->quit();
qDebug()<<"clicked m_thread quit";
}
void Widget::on_terminateButton_clicked()
{
m_thread->terminate();
qDebug()<<"clicked m_thread terminate";
}
我又加了3个按钮在thread执行起来的时候我们挨个点击一下看一下
exit
很明显我们点击了exit以后线程并没有退出
quit
一样的quit也不会退出线程- terminate
terminate 确实终止了线程,但是这个函数有非常大的不确定因素,非常不安全,并不推荐使用,下面我们说一下安全的退出线程的方法。
加一个bool 开关来限制线程的执行,但是如果同时改变这个bool ,可能造成冲突,我们需要加一个互斥锁
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QThread>
#include <QMutex>
class myThread :public QThread
{
Q_OBJECT
public:
myThread(QObject* parent);
void run();//只有run()里面在新的线程里
void work();
void stopThread();
signals:
void currentProgress(int value);
private:
bool m_isRun = true;
QMutex *m_mutex;
};
#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
myThread::myThread(QObject *parent)
{
qDebug()<<"construction ThreadID:"<<QThread::currentThreadId();
m_mutex = new QMutex();
}
void myThread::run()
{
for(int i = 0;i<=10;i++)
{
if(!m_isRun)
return;
qDebug()<<"QThread::run() ThreadID:"<<QThread::currentThreadId();
emit currentProgress(i * 10);
msleep(200);
}
}
void myThread::work()
{
qDebug()<<"do work ThreadID:"<<QThread::currentThreadId();
}
void myThread::stopThread()
{
QMutexLocker(*m_mutex);
m_isRun = false;
}
这样我们在run里面循环每次执行的时候判断一下开关的状态,这样退出线程是比较优雅的
下面我们在介绍一下全局线程和局部线程的释放
全局线程就是当程序启动时线程随着程序启动,当程序关闭时线程结束(比如:检测网络心跳包 串口通信)
这时需要注意,别手动 delete 线程指针。有可能你要删除的这个对象在Qt的事件循环里还排队,但你却已经在外面删除了它,这样程序会发生崩溃。
我们改一下上面的程序使其成为全局线程
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QObject>
#include <QThread>
#include <QMutex>
class myThread :public QThread
{
Q_OBJECT
public:
myThread(QObject* parent);
void run();//只有run()里面在新的线程里
void stopThread();
signals:
void currentProgress(int value);
private:
bool m_isRun = true;
QMutex *m_mutex;
};
#endif // MYTHREAD_H
#include "mythread.h"
#include <QDebug>
myThread::myThread(QObject *parent)
{
qDebug()<<"construction ThreadID:"<<QThread::currentThreadId();
m_mutex = new QMutex();
}
void myThread::run()
{
int i = 10;
while(1)
{
if(!m_isRun)
return;
qDebug()<<"QThread::run() ThreadID:"<<QThread::currentThreadId();
emit currentProgress(i);
i += 10;
if(i >= 100)
i = 0;
msleep(200);
}
}
void myThread::stopThread()
{
QMutexLocker(*m_mutex);
m_isRun = false;
}
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include "mythread.h"
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
private slots:
void receiveProgress(int value);
private:
Ui::Widget *ui;
myThread* m_thread;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
qDebug()<<"UI widget ThreadId:"<<QThread::currentThreadId();
ui->progressBar->setValue(0);
m_thread = new myThread(this);//线程的父类是UI 窗口类
connect(m_thread,&myThread::currentProgress,this,&Widget::receiveProgress);
m_thread->start();
}
Widget::~Widget()
{
delete m_thread; //!!!!! 非常危险 !!!!!
delete ui;
}
void Widget::receiveProgress(int value)
{
ui->progressBar->setValue(value);
}
我们在析构函数里面 直接 delete m_thread 这样非常危险
QThread:Destoryed while thread is still running
线程还在运行我们就给他destory所以会出问题
我们只需要在析构里面 加上我们上面写的 m_thread->stopThread();
Widget::~Widget()
{
m_thread->stopThread();
m_thread->wait();
delete m_thread;
delete ui;
}
m_thread->wait();
是阻塞主线程 (一直等到子线程结束)当 子线程安全退出后 才会继续像下面执行,这样delete 时候就不会发生错误了
局部线程(处理卡界面耗时的操作 数据的读取解析,耗时操作)
局部线程就是 在一个函数体内执行的 运行完线程就结束了
QThread自己删除自己,就是在new线程时,不指定父对象,通过绑定void QObject::deleteLater () [slot]槽让它自动释放。这样在widget析构时可以免去m_thread->wait();这句。
void Widget::on_doWorkButton_clicked()
{
//局部线程
myThread* m_thread = new myThread(NULL);
connect(m_thread,&myThread::currentProgress,this,&Widget::receiveProgress);
connect(m_thread,&myThread::finished,m_thread,&myThread::deleteLater);
m_thread->start();
}
这样当线程跑完退出以后就delete了,不需要我们手动处理了
下一篇在介绍 moveTothread 方法 https://blog.csdn.net/weixin_42837024/article/details/81411178
98年菜鸡一枚总结不到位请大佬们多指教!