天天看点

Qt创建线程之继承QThread方式一、QThread说明二、QThread的主要成员函数三、继承QThread线程创建四、窥探terminate()后的现象五、如何优雅的退出线程

目录

一、QThread说明

二、QThread的主要成员函数

三、继承QThread线程创建

四、窥探terminate()后的现象

五、如何优雅的退出线程

一、QThread说明

QThread是Qt的一个跨平台解决多线程方案,简单易学,可以让初学者很快上手。QThread是Qt里封装好的一个类,那么既然是类,那么继承QThread的线程就必须以对象的形式被创建和使用,每一个线程都应该对应一个对象。

二、QThread的主要成员函数

1、void QThread::run()函数:线程函数体,用于定义线程功能,该函数是线程的执行入口,将一切复杂耗时的业务处理放到该方法当中;

Qt创建线程之继承QThread方式一、QThread说明二、QThread的主要成员函数三、继承QThread线程创建四、窥探terminate()后的现象五、如何优雅的退出线程

2、void QThread::start()函数:启动函数,将线程入口地址设置为run()函数的地址;

Qt创建线程之继承QThread方式一、QThread说明二、QThread的主要成员函数三、继承QThread线程创建四、窥探terminate()后的现象五、如何优雅的退出线程

3、void QThread::terminate()函数:强制退出当前线程(不推荐使用),假设如果在线程当中分配了内存,使用该方法可能会导致在线程退出前,没有释放这块内存,就强制退出,这就会造成内存泄露,这也就是不推荐使用terminate()这个方法的根本原因。

Qt创建线程之继承QThread方式一、QThread说明二、QThread的主要成员函数三、继承QThread线程创建四、窥探terminate()后的现象五、如何优雅的退出线程

三、继承QThread线程创建

1、继承QThread类,重写run()函数;

class MyThread : public QThread
{
protected:

    void run()
    {
        qDebug() << "run() begin...";

        for( int i=0; i<10; i++ )
        {
            qDebug() << objectName() << ": " << i;
            sleep(1);
        }

        qDebug() << "run() end...";
    }
};
           

2、在主线程创建MyThread类对象,并调用start()方法,这就能完整的创建好一个继承QThread的线程。

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main begin...";

    MyThread t;
    t.start();

    qDebug() << "main end...";
    return a.exec();
}
           

四、窥探terminate()后的现象

#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
protected:

    void run()
    {
        qDebug() << "run() begin...";

        // 申请内存
        int *ret = new int[10000];

        for( int i=0; i<10; i++ )
        {
            qDebug() << objectName() << ": " << i;
            sleep(1);
            ret[i] = i*i;
        }

        qDebug() << "delete ret...";
        delete []ret;
        qDebug() << "run() end...";
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main begin...";

    MyThread t;
    t.setObjectName("t");
    t.start();

    QThread::sleep(5);
    t.terminate();

    qDebug() << "main end...";
    return a.exec();
}
           
Qt创建线程之继承QThread方式一、QThread说明二、QThread的主要成员函数三、继承QThread线程创建四、窥探terminate()后的现象五、如何优雅的退出线程

从结果来看,程序输出结果并没有打印"delete ret,,,"和"run() end...",可想而知在terminate强制退出后,ret所指向的内存并没有得到释放,造成内存泄露。而且这类产生的bug估计很难察觉到,毕竟很难想到会在主线程里使用terminate()进行强制退出,而造成的内存泄露。

在工程开发中,terminate()函数是不推荐使用,或者是禁止使用的!terminate()会使得操作系统暴力停止线程,而不会考虑数据的完整性,以及资源释放等问题。

五、如何优雅的退出线程

1、run()函数执行结束是优雅终止线程的唯一方式(不仅仅局限于Qt的多线程);

2、在线程中增加标志变量m_toStop(volatile bool);

3、通过判断m_toStop的值判断是否需要从run()函数返回。

#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
    volatile bool m_toStop;

public:
    MyThread()
    {
        m_toStop = false;
    }

    // 将标志位设置为true,并优雅终止线程run()函数
    void Stop()
    {
        m_toStop = true;
    }

protected:

    void run()
    {
        qDebug() << "run() begin...";

        // 申请内存
        int *ret = new int[10000];

        for( int i=0; ((!m_toStop) && (i<10)); i++ )
        {
            qDebug() << objectName() << ": " << i;
            sleep(1);
            ret[i] = i*i;
        }

        qDebug() << "delete ret...";
        delete []ret;
        qDebug() << "run() end...";
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug() << "main begin...";

    MyThread t;
    t.setObjectName("t");
    t.start();

    QThread::sleep(5);
    // 调用Stop()方法
    t.Stop();

    qDebug() << "main end...";
    return a.exec();
}
           
Qt创建线程之继承QThread方式一、QThread说明二、QThread的主要成员函数三、继承QThread线程创建四、窥探terminate()后的现象五、如何优雅的退出线程

从输出结果可以看出,程序在延迟五秒之后,顺利直接完了run()函数,并成功释放了ret所指向的内存,没有造成内存泄露,这就是优雅终止线程的唯一方式。