zoukankan      html  css  js  c++  java
  • 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开发中,在其他的平台开发中,只要涉及多线程都可以考虑这种解决方案。

  • 相关阅读:
    装饰器wraps
    装饰器的语法糖
    VisionPro 自学帮助
    准备重新编译VisionPro 官方版本 使用 vs2019
    个人随笔_学习感悟
    VisionPro 一些图操作
    System.Data.Sqlite.dll 使用异常问题参考
    NOPI笔记01
    VisionPro帮助文档学习01(UserGuide DisPlay)
    MSSqlServer基础学习01
  • 原文地址:https://www.cnblogs.com/-glb/p/13363865.html
Copyright © 2011-2022 走看看