zoukankan      html  css  js  c++  java
  • QT线程的结束

    原贴地址:https://blog.csdn.net/caoshangpa/article/details/62421334

    感谢原作者的帮助!

    如果一个线程运行完成,就会结束。可很多情况并非这么简单,由于某种特殊原因,当线程还未执行完时,我们就想中止它。
    不恰当的中止往往会引起一些未知错误。比如:当关闭主界面的时候,很有可能次线程正在运行,这时,就会出现如下提示:
    QThread: Destroyed while thread is still running
    这是因为次线程还在运行,就结束了UI主线程,导致事件循环结束。这个问题在使用线程的过程中经常遇到,尤其是耗时操作。
    在此问题上,常见的两种人:
    1.直接忽略此问题。
    2.强制中止 - terminate()。
    大多数情况下,当程序退出时,次线程也许会正常退出。这时,虽然抱着侥幸心理,但隐患依然存在,也许在极少数情况下,就会出现Crash。
    正如前面提到过terminate(),比较危险,不鼓励使用。线程可以在代码执行的任何点被终止。线程可能在更新数据时被终止,从而没有机会来清理自己,解锁等等。。。总之,只有在绝对必要时使用此函数。
    所以,我们应该采取合理的措施来优雅地结束线程,一般思路:
    1.发起线程退出操作,调用quit()或exit()。
    2.等待线程完全停止,删除创建在堆上的对象。
    3.适当的使用wait()(用于等待线程的退出)和合理的算法。
    下面介绍两种方式:
    一.QMutex互斥锁 + bool成员变量。
    这种方式是Qt4.x中比较常用的,主要是利用“QMutex互斥锁 + bool成员变量”的方式来保证共享数据的安全性(可以完全参照下面的requestInterruption()源码写法)。

    #include <QThread>
    #include <QMutexLocker>
     
    class WorkerThread : public QThread
    {
        Q_OBJECT
     
    public:
        explicit WorkerThread(QObject *parent = 0)
            : QThread(parent),
              m_bStopped(false)
        {
            qDebug() << "Worker Thread : " << QThread::currentThreadId();
        }
     
        ~WorkerThread()
        {
            stop();
            quit();
            wait();
        }
     
        void stop()
        {
            qDebug() << "Worker Stop Thread : " << QThread::currentThreadId();
            QMutexLocker locker(&m_mutex);
            m_bStopped = true;
        }
     
    protected:
        virtual void run() Q_DECL_OVERRIDE {
            qDebug() << "Worker Run Thread : " << QThread::currentThreadId();
            int nValue = 0;
            while (nValue < 100)
            {
                // 休眠50毫秒
                msleep(50);
                ++nValue;
     
                // 准备更新
                emit resultReady(nValue);
     
                // 检测是否停止
                {
                    QMutexLocker locker(&m_mutex);
                    if (m_bStopped)
                        break;
                }
                // locker超出范围并释放互斥锁
            }
        }
    signals:
        void resultReady(int value);
     
    private:
        bool m_bStopped;
        QMutex m_mutex;
    };

    为什么要加锁?很简单,是为了共享数据段操作的互斥。
    何时需要加锁?在形成资源竞争的时候,也就是说,多个线程有可能访问同一共享资源的时候。
    当主线程调用stop()更新m_bStopped的时候,run()函数也极有可能正在访问它(这时,他们处于不同的线程),所以存在资源竞争,因此需要加锁,保证共享数据的安全性。

    二.Qt5以后:requestInterruption() + isInterruptionRequested()
    这两个接口是Qt5.x引入的,使用很方便:

    class WorkerThread : public QThread
    {
        Q_OBJECT
     
    public:
        explicit WorkerThread(QObject *parent = 0)
            : QThread(parent)
        {
        }
     
        ~WorkerThread() {
            // 请求终止
            requestInterruption();
            quit();
            wait();
        }
     
    protected:
        virtual void run() Q_DECL_OVERRIDE {
            // 是否请求终止
            while (!isInterruptionRequested())
            {
                // 耗时操作
            }
        }
    };

    在耗时操作中使用isInterruptionRequested()来判断是否请求终止线程,如果没有,则一直运行;当希望终止线程的时候,调用requestInterruption()即可。
    正如侯捷所言:「源码面前,了无秘密」。如果还心存疑虑,我们不妨来看看requestInterruption()、isInterruptionRequested()的源码:

    void QThread::requestInterruption()
    {
        Q_D(QThread);
        QMutexLocker locker(&d->mutex);
        if (!d->running || d->finished || d->isInFinish)
            return;
        if (this == QCoreApplicationPrivate::theMainThread) {
            qWarning("QThread::requestInterruption has no effect on the main thread");
            return;
        }
        d->interruptionRequested = true;
    }
     
    bool QThread::isInterruptionRequested() const
    {
        Q_D(const QThread);
        QMutexLocker locker(&d->mutex);
        if (!d->running || d->finished || d->isInFinish)
            return false;
        return d->interruptionRequested;
    }

    补充1:

    对于wait并不提倡使用,为什么?因为在不满足return的条件之前会一直阻塞线程。并且qt是基于事件框架的,所以建议使用侦听finished信号来替代wait

    补充2:

    想补充一些,今天项目遇到的,就第一种方法来说,用不用锁可以看情况,不一定必须要使用,因为在whlie里面加锁,有时候会因为你线程一直抢到锁导致你的stop并没有用。尤其是你很多时候会在whlie里面加continue的情况==。如果只是主线程和这一个线程使用到stop,那么完全可以不用锁,你最坏的情况就是while会多跑一次。就看这一次对于你的影响大还是长时间得不到锁的影响大。就比如这段代码==天坑,鬼知道我经历了什么

    while( true )
      {
     
       int iFailureCount = 0;
       {
        //stop process
        QMutexLocker locker1( &m_stReleaseMutex );
        if(m_bStopProcess)
         break;
        QMutexLocker locker2( m_pSync );
        if( m_listTextArray.isEmpty())
        {
    #ifdef PROC_EFFI_TEST
         {
          itime = t.restart();
          out &lt;&lt;  atime &lt;&lt; "
    " ;
         }
    #endif
         break;
         //continue;
        }
        
     
           m_listTextArrayPicked = m_listTextArray;
        listTextArray2 = m_listTextArrayPicked;
        m_listTextArray.clear();
     
       }
  • 相关阅读:
    zabbix监控部署(三)
    zabbix监控部署(二)
    zabbix监控部署(一)
    Docker安装mysql5.7
    Docker修改容器默认存储路径
    MySQL全量+增量备份脚本
    企业级Dokcer镜像仓库Harbor部署
    Linux环境pyhon3安装pyinstaller
    配置Hive元数据数据库为PostgreSQL
    Oracle 11gR2 RAC 添加节点
  • 原文地址:https://www.cnblogs.com/myboat/p/14536162.html
Copyright © 2011-2022 走看看