Qt 多线程第一种实现方式继承QThread 重写run方法

Qt 多线程有两种实现方法:

1.继承QThread 重写run()方法:
run()线run(),
2.继承QObject moveToThread
Qt4.8Qt使,

本篇介绍 第一种 继承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年菜鸡一枚总结不到位请大佬们多指教!
  • 5
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值