Qt4和Qt5的多线程使用方法有些许差异,Qt5的线程使用方式更加灵活。
首先初始化一个Qt Application Widget,并添加一个类命名为mythread,基类为Qobject。
mythread类具体实现如下:
class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);
//线程处理函数
void myTimeout();
void setFlag(bool flag = true);
signals:
void mySignal();
private:
bool isStop;
};
#include "mythread.h"
#include <QThread>
#include <QMessageBox>
//宏定义打印调试
#include<QtDebug>
#define cout qDebug() <<"["<<__FILE__<<":"<<__LINE__<<"]"
MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop =false;
}
void MyThread::myTimeout()
{
while(isStop == false)
{
//线程运行函数不可以操作图形界面,编译可通过,运行会报错
//QMessageBox::aboutQt(NULL);
QThread::sleep(1);
emit mySignal();
cout<<"子线程号:"<<QThread::currentThread();
// if(true == isStop)
// {
// break;
// }
}
}
void MyThread::setFlag(bool flag)
{
isStop = flag;
}
widget类的实现代码如下:
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
void dealSignal(); //信号处理
void dealClose(); //关闭窗口处理
signals:
void startThread(); //开启线程
private slots:
void on_pushButton_clicked(); //按钮start
void on_pushButton_2_clicked(); //按钮stop
private:
Ui::Widget *ui;
MyThread *myT;
QThread *thread;
};
#include "widget.h"
#include "ui_widget.h"
//宏定义打印调试
#include<QtDebug>
#define cout qDebug() <<"["<<__FILE__<<":"<<__LINE__<<"]"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//指定父对象,会导致自定义线程无法加入到子线程中
//myT = new MyThread(this);
//动态分配空间,不能指定父对象
myT =new MyThread;
//创建子线程
thread = new QThread(this);
//自定义线程加入到子线程中
myT->moveToThread(thread);
connect(myT, &MyThread::mySignal, this, &Widget::dealSignal);
cout<<"connect1";
cout<<"主线程号:"<<QThread::currentThread();
connect(this, &Widget::startThread, myT, &MyThread::myTimeout);
cout<<"connect2";
connect(this, &Widget::destroyed, this, &Widget::dealClose);
cout<<"connect3";
//connect函数第五个参数的作用
//多线程时才有意义,指定连接方式,共有三种,Qt::ConnectionType
//默认的时候,如果是多线程,默认使用队列,如果是单线程,默认使用直接方式
//队列:槽函数所在线程和接受者一样
//直接:槽函数所在线程和发送者一样
}
Widget::~Widget()
{
delete ui;
}
void Widget::dealClose()
{
on_pushButton_2_clicked();
delete myT;
cout<<"关闭窗口,回收线程";
}
void Widget::on_pushButton_clicked()
{
if(thread->isRunning() == true)
{
cout<<"线程已经处于启动状态";
return;
}
//启动线程,没有启动线程处理函数
thread->start();
myT->setFlag(false);
//不能直接调用线程处理函数,直接调用导致线程处理函数和主线程是在同一个线程
//myT->myTimeout();
//只能通过信号和槽的方式调用
emit startThread();
}
void Widget::dealSignal()
{
static int i = 0;
i++;
ui->lcdNumber->display(i);
}
void Widget::on_pushButton_2_clicked()
{
if(thread->isRunning() == false)
{
cout<<"线程已经处于关闭状态";
return;
}
cout<<"stop,回收线程";
myT->setFlag(true);
thread->quit();
thread->wait();
}
UI界面如下:
widget默认为主线程,从主线程创建一个mythread和一个QThread,通过movetothread()函数将子线程myT放入thread中运行。通过isStop标志位控制子线程的开启和关闭。子线程通过while循环控制LCD显示数字累加。
注意事项:
1.子线程的开启必须通过信号与槽的机制运行,不能直接调用线程处理函数,直接调用导致线程处理函数和主线程是在同一个线程;
2.初始化myThread时不能指定父对象(this),否则报错;
3.线程运行函数不可以操作图形界面,编译可通过,运行会报错;
4.connect()函数有第五个参数,第五个参数在多线程情况下使用,用于指定线程连接方式Qt::ConnectionType;
5.运行结束后,要考虑线程和类的回收( thread->quit(); thread->wait();),要考虑stop和关闭窗口两种情况,stop按钮通过槽函数实现;关闭窗口通过Widget::destroyed信号触发槽函数实现;
6.通过thread->isRunning(),判断线程运行状态,避免start和stop出现冗余操作。
视频教程参考(p78-p80):QT开发全套视频_哔哩哔哩_bilibili