QT多线程的实现方式:QThread run优雅的创建与退出【QT多线程】

qt通过继承实现线程的方法有两种

  1. 继承QThread,然后重写run函数实现多线程
  2. 继承QObject,使用moveToThread函数实现多线程

本文介绍第一种的创建、使用与退出。

一、QThread类的run介绍

1、QThread::run函数的使用

QThread 是用来管理线程的,它所依附的线程和它管理的新线程并不是同一个东西QThread 所依附的线程,就是执行创建QThread的线程。也就是咱们这儿的主线程,QThread 管理的新线程,就是 run 启动的线程。

所以总结一句话:QThread只有run函数是在新线程里的,其他所有函数都在QThread生成的线程里

那么就抛出两个问题

1.QThread的对象依附在主线程中,次线程的slot函数会在主线程中执行,而不是次线程。除非:(不建议这样做)

  • QThread 对象通过movetoThread依附到次线程中(这里涉及到实现多线程的第二种方式)
  • slot 和信号是直接连接,且信号在次线程中发射(这里涉及到QTconnect的第五个参数)

2.QThread的继承类的其他函数尽量别要有太耗时的操作,要确保所有耗时的操作都在run函数里

2、QThread run方式特点

2.1优点:可以通过信号槽与外界进行通信。
2.2缺点:

每次新建一个线程都需要继承QThread,实现一个新类,使用不太方便。
要自己进行资源管理,线程释放和删除。并且频繁的创建和释放会带来比较大的内存开销。

2.3适用场景:QThread适用于那些常驻内存的任务。

二、QThread run实现一个简单线程

新建一个集成QThread的类,重写虚函数run,通过run启动线程

代码:https://download.csdn.net/download/qq_43445867/88332781

1、基本流程

#ifndef QTHREAD_RUN_H
#define QTHREAD_RUN_H


class QThread_Run
{
public:
    QThread_Run();
};

#endif // QTHREAD_RUN_H

1.1需要创建一个线程类的子类,让其继承 QT 中的线程类 QThread,比如:

#ifndef QTHREAD_RUN_H
#define QTHREAD_RUN_H

#include <QThread>

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

};

#endif // QTHREAD_RUN_H

 1.2 重写父类的 run () 方法,在该函数内部编写子线程要处理的具体的业务流程

#ifndef QTHREAD_RUN_H
#define QTHREAD_RUN_H

#include <QThread>

class QThread_Run : public QThread
{
    Q_OBJECT
    
public:
    explicit QThread_Run(QObject *parent = nullptr);
    QThread_Run();
    
protected:
    void run();
};

#endif // QTHREAD_RUN_H

1.3 在主线程中创建子线程对象,new 一个就可以了

QThread_Run* qthread_run=new QThread_Run(this);

1.4 启动子线程,调用 start () 方法

qthread_run->start();

不能在类的外部调用 run () 方法启动子线程,在外部调用 start () 相当于让 run () 开始运行

2、完整代码

qthread_run.h

#ifndef QTHREAD_RUN_H
#define QTHREAD_RUN_H

#include <QThread>

class QThread_Run : public QThread
{
    Q_OBJECT

public:
    explicit QThread_Run(QObject *parent = nullptr);
    QThread_Run();

protected:
    void run();

signals:
    void result(int);
};

#endif // QTHREAD_RUN_H

qthread_run.c

#include "qthread_run.h"
#include <QEventLoop>
#include <QTimer>

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

}
void Delay_MSec(unsigned int msec)
{
    QEventLoop loop;//定义一个新的事件循环
    QTimer::singleShot(msec, &loop, SLOT(quit()));  //创建单次定时器,槽函数为事件循环的退出函数
    loop.exec();  //事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}

void  QThread_Run::QThread_Run::run()
 {
   for(int i=0;i<10000;i++)
   {
       emit result(i);
       Delay_MSec(100);
   }
 }

mainwindow.c

#include "mainwindow.h"
#include "ui_mainwindow.h"

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


    QThread_Run* qthread_run=new QThread_Run(this);

    connect(qthread_run,&QThread_Run::result,this,[=](int num)
    {
      ui->lcdNumber->display(num);
    });

    connect(ui->pushButton,&QPushButton::clicked, this, [=]()
    {
        qthread_run->start();
    });

}

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

mainwindow.h

#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "qthread_run.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private:
    Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H

三、优雅的停止创建的线程

quitexit函数都不会中途终端线程,要马上终止一个线程可以使用terminate函数,但这个函数存在非常不安定因素,会提示以下错误

QObject::killTimer: Timers cannot be stopped from another thread

停止创建的线程分两种情况

1、不使用事件循环

run函数内有一个 while 或 for 的死循环

最简单的方法是添加一个bool变量,设置一个标记来控制死循环的退出,通过主线程修改这个bool变量来进行终止,但这样有可能引起访问冲突,需要加锁

完整代码:https://download.csdn.net/download/qq_43445867/88332782

需要在原来的头文件加上如下语句

 qthread_run.h

#ifndef QTHREAD_RUN_H
#define QTHREAD_RUN_H

#include <QThread>

class QThread_Run : public QThread
{
    Q_OBJECT

public:
    explicit QThread_Run(QObject *parent = nullptr);
    QThread_Run();
    bool m_isCanRun;//加入标志位
//加入槽函数
public slots:
    void stopImmediately();

protected:
    void run();

signals:
    void result(int);
};

#endif // QTHREAD_RUN_H

修改源文件以及run函数

 qthread_run.c

#include "qthread_run.h"
#include <QEventLoop>
#include <QTimer>

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

}
void Delay_MSec(unsigned int msec)
{
    QEventLoop loop;//定义一个新的事件循环
    QTimer::singleShot(msec, &loop, SLOT(quit()));  //创建单次定时器,槽函数为事件循环的退出函数
    loop.exec();  //事件循环开始执行,程序会卡在这里,直到定时时间到,本循环被退出
}
//增加槽函数处理
void QThread_Run::stopImmediately()
{
    QMutexLocker locker(&m_lock);
    m_isCanRun = false;
}

void  QThread_Run::QThread_Run::run()
 {
   for(int i=0;i<10000;i++)
   {
      //增加判断
      QMutexLocker locker(&m_lock);
      if(!m_isCanRun)//在每次循环判断是否可以运行,如果不行就退出循环
      {
           return;
      }
       emit result(i);
       Delay_MSec(100);
   }
 }

猜你喜欢

转载自blog.csdn.net/qq_43445867/article/details/132797225