Qt-线程的使用
阅读原文时间:2023年07月10日阅读:2

1  简介

参考视频:https://www.bilibili.com/video/BV1XW411x7NU?p=74

使用多线程的好处:提高应用程序响应速度、使多CPU更加高效、改善程序结构。

在Qt中使用QThread来管理线程。Qt中使用线程时,需要自己实现一个thread的类。

2  测试说明

(1)基本使用

功能说明如下:

工程文件有:

mythread.h和mythread.cpp是自定义的线程类,需要改为继承自QThread,QThread类有一个虚函数run(),它就是线程处理函数,我们需要重写它。

当我们调用QThread的start()函数时,会间接的调用run()函数。

widget.h和widget.cpp是主窗口的代码。

mythread.h的代码:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include
#include

class MyThread : public QThread
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);

signals:
void isDone();

protected:
//QThread的虚函数,线程处理函数
//不能直接调用,通过start()间接调用
void run();

public slots:
};

#endif // MYTHREAD_H

mythread.cpp代码:

#include "mythread.h"

MyThread::MyThread(QObject *parent) : QThread(parent)
{

}

void MyThread::run()
{
//很复杂的数据处理,需要耗时5s
sleep();
//发送处理完成信号
emit isDone();
}

widget.h代码:

#ifndef WIDGET_H
#define WIDGET_H

#include
#include //定时器
#include "mythread.h" //线程

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = );
~Widget();

 void dealTimeout(); //定时器处理函数  
 void dealThread();  //处理子线程发来的信号  
 void stopThread();  //停止线程

private slots:
void on_pushButton_start_clicked();

private:
Ui::Widget *ui;

 QTimer \*timer = NULL;  
 MyThread \*thread = NULL;  

};

#endif // WIDGET_H

widget.cpp代码:

#include "widget.h"
#include "ui_widget.h"
#include
#include

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);

 timer = new QTimer(this);  
 //分配空间  
 thread = new MyThread(this);

 //只要定时器启动,自动触发timerout()信号  
 connect(timer, &QTimer::timeout, this, &Widget::dealTimeout);  
 //接收子线程发送的isDone信号并处理  
 connect(thread, &MyThread::isDone, this, &Widget::dealThread);  
 //当按窗口右上角x时(关闭窗口),触发  
 connect(this, &Widget::destroyed, this, &Widget::stopThread);  

}

Widget::~Widget()
{
delete ui;
}

void Widget::dealTimeout()
{
static int i = ;
i++;
//设定lcd的值
ui->lcdNumber->display(i);
}

void Widget::dealThread()
{
//处理完数据后,关闭定时器
timer->stop();
qDebug() << "timer turn off!!!";
}

void Widget::stopThread()
{
//停止线程
thread->quit();
//等待线程处理完事情
thread->wait();
}

void Widget::on_pushButton_start_clicked()
{
if (timer->isActive() == false) {
timer->start();
}
//启动线程,处理数据
thread->start();
}

运行测试:

可以看到计数了45次之后(每次100ms),定时器停止了,表示接收到了线程发送的信号(线程睡眠5s之后发出的)。可能会有疑问为什么不是50次(刚好5s),那是因为我们先启动定时器,在去启动线程,这个过程需要花费时间。

(2)多线程的使用

多线程的实现模型如下,记下来就好了:

先给出实现的代码,后面再介绍。

工程文件有:

mythread.h和mythread.cpp是自定义的线程类,继承自QThread,和前一个例子不一样。

widget.h和widget.cpp是主窗口的代码。

mythread.h代码:

#ifndef MYTHREAD_H
#define MYTHREAD_H

#include

class MyThread : public QObject
{
Q_OBJECT
public:
explicit MyThread(QObject *parent = nullptr);

 //线程处理函数  
 void myTimerout();  
 //设置flag,用于判断是否结束线程处理函数的while循环  
 void setFlag(bool flag = true);

signals:
void mySignal();

public slots:

private:
bool isStop;
};

#endif // MYTHREAD_H

mythread.c代码:

#include "mythread.h"
#include
#include

MyThread::MyThread(QObject *parent) : QObject(parent)
{
isStop = false;
}

void MyThread::myTimerout()
{
while (isStop == false) {
QThread::sleep();
emit mySignal();
qDebug() << "子线程号:" << QThread::currentThread();
if (true == isStop) {
break;
}
}
}

void MyThread::setFlag(bool flag)
{
isStop = flag;
}

widget.h代码:

#ifndef WIDGET_H
#define WIDGET_H

#include
#include "mythread.h"
#include

namespace Ui {
class Widget;
}

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = );
~Widget();

 void dealsignal();  
 void dealclose();  

signals:
//启动子线程的信号
void startThreadSignal();

private slots:
void on_pushButton_start_clicked();

 void on\_pushButton\_stop\_clicked();

private:
Ui::Widget *ui;
MyThread *mythread = NULL;
QThread *thread = NULL;
};

#endif // WIDGET_H

widget.cpp代码:

#include "widget.h"
#include "ui_widget.h"
#include

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
//动态分配空间,不能指定父对象
mythread = new MyThread;
//创建子线程
thread = new QThread(this);
//把自定义的线程加入到子线程中
mythread->moveToThread(thread);

 //处理子线程发送的信号  
 connect(mythread, &MyThread::mySignal, this, &Widget::dealsignal);  
 qDebug() << "主线程号:" << QThread::currentThread();  
 //发送信号给子线程,通过信号和槽调用子线程的线程处理函数  
 connect(this, &Widget::startThreadSignal, mythread, &MyThread::myTimerout);  
 //关闭主窗口  
 connect(this, &Widget::destroyed, this, &Widget::dealclose);  

}

Widget::~Widget()
{
delete ui;
}

void Widget::dealsignal()
{
static int i = ;
i++;
ui->lcdNumber->display(i);
}

void Widget::dealclose()
{
mythread->setFlag(true);
thread->quit();
thread->wait();
delete mythread;
}

void Widget::on_pushButton_start_clicked()
{
if (thread->isRunning() == true) {
return;
}
//启动线程,但是没有启动线程处理函数
thread->start();
mythread->setFlag(false);
//不能直接调用线程处理函数
//直接调用导致线程处理函数和主线程在同一个线程
//只能通过信号和槽调用
emit startThreadSignal();
}

void Widget::on_pushButton_stop_clicked()
{
if (thread->isRunning() == false) {
return;
}
mythread->setFlag(true);
thread->quit();
thread->wait();
}

需要说明以下几点:

(1)创建自定义线程变量时,不能指定父对象(mythread=newMyThread;),因为调用moveToThread函数之后,变量在创建的线程中使用回收,而不是在主线程。

(2)子线程会睡眠1s就发送一个信号mySignal给主线程,主线程接收信号,在槽函数中将数值累加一,并在LCD上显示。

(3)主线程通过信号和槽的方式调用子线程处理函数,主线程发送信号给子线程,槽函数就是子线程的线程处理函数。

(4)子线程setFlag()的作用是:关闭子线程时,quit()函数会等待子线程执行结束之后再回收,但是如果不设置标志,while循环会一直执行,子线程也就没有结束。

运行测试:

注意看打印的信息,可以看到子线程和主线程的id。

手机扫一扫

移动阅读更方便

阿里云服务器
腾讯云服务器
七牛云服务器

你可能感兴趣的文章