Qt中的多线程编程

Qt中的多线程编程

 

 

 QThread编程示例

复制代码
class MyThread: public QThread   //创建线程类
{
protected:
    void run()   //线程入口函数
    {
        for(int i=0; i<5; i++)
        {
            qDebug() << objectName() << ":" << i;
            sleep(1)  //暂停1s
        }
    }

};
复制代码

多线程编程初探

实例1:

复制代码
#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << " : " << "run() begin";
        for(int i=0; i<5; i++)
        {
            qDebug() << objectName() << ": " << i;
            sleep(1);
        }
        qDebug() << objectName() << " : " << "run() end";
    }

};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main() begin";

    MyThread t;
    t.setObjectName("t");
    t.start();   //创建一个线程,并执行线程体run函数

    qDebug() << "main() end";

    return a.exec();
}
复制代码

 

示例中的主线程将先于子线程结束,所有线程都结束后,进程结束

 实例2

复制代码
#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << " : " << "run() begin";
        for(int i=0; i<5; i++)
        {
            qDebug() << objectName() << ": " << i;
            sleep(1);
        }
        qDebug() << objectName() << " : " << "run() end";
    }


};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main() begin";

    MyThread t;
    t.setObjectName("t");
    t.start();   //创建一个线程,并执行线程体run函数

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

    for(int i=0; i<100000; i++)
    {
        for(int j=0; j<10000; j++)
        {

        }
    }


    qDebug() << "main() end";

    return a.exec();
}
复制代码

第一次运行结果:

 第二次运行结果

 从上面的运行结果看,每次运行结果都不同。

在主线程中创建的两个子线程是并行执行的,这两个线程间没有交互,各自执行。这就是线程间的并行性

 重要注意:

在工程开发中terminate是禁止使用的,terminate会使得操作系统暴力终止线程,而不考虑数据的完整性、资源释放等问题。

问题:如何在代码中优雅的终止线程

解决方案思路:

-run函数执行结束是优雅终止线程的唯一方式

-在线程类中增加标识变量m_toStop(volatile bool)每次都需要从内存中读取,不需要编译器做任何的优化。

-通过m_toStop的值判断是否需要从run函数返回

复制代码
class Solution : public QThread
{
protected:
    volatile bool m_toStop;
    void run();
public:
    Solution()
    {
        m_toStop = false;
    }
    
    void stop()
    {
        m_toStop = true;
    }

};

void Solution::run()
{
    while(!m_toStop)
    {
        //do task
    }
}
复制代码

首先看一下暴力终止线程的例子:

复制代码
#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << " : " << "run() begin";
        for(int i=0; i<20; i++)
        {
            qDebug() << objectName() << ": " << i;
            msleep(500);
        }
        qDebug() << objectName() << " : " << "run() end";
    }


};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main() begin";

    MyThread t;
    t.setObjectName("t");
    t.start();   //创建一个线程,并执行线程体run函数

    for(int i=0; i<100000; i++)
    {
        for(int j=0; j<10000; j++)
        {

        }
    }

    t.terminate();
    qDebug() << "main() end";

    return a.exec();
}
复制代码

 在子线程中需要执行20次的,但当使用了terminate()暴力终止线程时,该线程出现了非正常死亡,该种方式不安全,也不优雅。

下面看一个暴力终止一个线程,带来内存泄露的问题。

复制代码
#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
protected:
    void run()
    {
        qDebug() << objectName() << " : " << "run() begin";

        int* p = new int[10000];

        for(int i=0; i<20; i++)
        {
            qDebug() << objectName() << ": " << i;
            msleep(500);
        }

        delete[] p;
        qDebug() << objectName() << " : " << "run() end";
    }
};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main() begin";

    MyThread t;
    t.setObjectName("t");
    t.start();   //创建一个线程,并执行线程体run函数

    for(int i=0; i<100000; i++)
    {
        for(int j=0; j<10000; j++)
        {

        }
    }

    t.terminate();
    qDebug() << "main() end";

    return a.exec();
}
复制代码

在子线程中,申请了一片堆空间,定义了10000个int型的数据。我们知道,堆空间只要申请了,就必须释放。但是上面这个程序释放了吗?看样子是真的释放了,但事实真的如此吗?看一下打印结果:

 从打印结果看,for循环只是运行了17次,就说明该子线程没有运行完,该线程就被暴力终止了。因此delete[] p肯定没有执行,申请了堆空间,没有被释放,那么将会产生内存泄露。

下面来看一下优雅的结束线程

复制代码
#include <QCoreApplication>
#include <QThread>
#include <QDebug>

class MyThread : public QThread
{
protected:
    volatile bool m_toStop;

    void run()
    {
        qDebug() << objectName() << " : " << "run() begin";

        int* p = new int[10000];

        for(int i=0; (!m_toStop)&&(i<20); i++)
        {
            qDebug() << objectName() << ": " << i;
            msleep(500);
        }

        delete[] p;
        qDebug() << objectName() << " : " << "run() end";
    }
public:
    MyThread()
    {
        m_toStop = false;
    }

    void stop()
    {
        m_toStop = true;
    }

};

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    qDebug() << "main() begin";

    MyThread t;
    t.setObjectName("t");
    t.start();   //创建一个线程,并执行线程体run函数

    for(int i=0; i<100000; i++)
    {
        for(int j=0; j<10000; j++)
        {

        }
    }

    t.stop();
    qDebug() << "main() end";

    return a.exec();
}
复制代码

使用t.stop优雅的结束线程,从打印结果看,t run() end被执行了,说明delete[] p被执行了,申请的堆空间被释放了。(注意,即使20次for循环没有被执行完,但是申请的堆空间被释放,达到了我们的要求)。

这种解决方案并不只适用Qt开发中,在其他的平台开发中,只要涉及多线程都可以考虑这种解决方案。

原文地址:https://www.cnblogs.com/bruce1992/p/14320451.html