• Qt优雅地结束线程(两种方法都是用Mutex锁住bool变量进行修改,然后由bool变量控制耗时动作的退出,即正常退出)


    如果一个线程运行完成,就会结束。可很多情况并非这么简单,由于某种特殊原因,当线程还未执行完时,我们就想中止它。
    不恰当的中止往往会引起一些未知错误。比如:当关闭主界面的时候,很有可能次线程正在运行,这时,就会出现如下提示:
    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()源码写法)。

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. #include <QThread>  
    2. #include <QMutexLocker>  
    3.   
    4. class WorkerThread : public QThread  
    5. {  
    6.     Q_OBJECT  
    7.   
    8. public:  
    9.     explicit WorkerThread(QObject *parent = 0)  
    10.         : QThread(parent),  
    11.           m_bStopped(false)  
    12.     {  
    13.         qDebug() << "Worker Thread : " << QThread::currentThreadId();  
    14.     }  
    15.   
    16.     ~WorkerThread()  
    17.     {  
    18.         stop();  
    19.         quit();  
    20.         wait();  
    21.     }  
    22.   
    23.     void stop()  
    24.     {  
    25.         qDebug() << "Worker Stop Thread : " << QThread::currentThreadId();  
    26.         QMutexLocker locker(&m_mutex);  
    27.         m_bStopped = true;  
    28.     }  
    29.   
    30. protected:  
    31.     virtual void run() Q_DECL_OVERRIDE {  
    32.         qDebug() << "Worker Run Thread : " << QThread::currentThreadId();  
    33.         int nValue = 0;  
    34.         while (nValue < 100)  
    35.         {  
    36.             // 休眠50毫秒  
    37.             msleep(50);  
    38.             ++nValue;  
    39.   
    40.             // 准备更新  
    41.             emit resultReady(nValue);  
    42.   
    43.             // 检测是否停止  
    44.             {  
    45.                 QMutexLocker locker(&m_mutex);  
    46.                 if (m_bStopped)  
    47.                     break;  
    48.             }  
    49.             // locker超出范围并释放互斥锁  
    50.         }  
    51.     }  
    52. signals:  
    53.     void resultReady(int value);  
    54.   
    55. private:  
    56.     bool m_bStopped;  
    57.     QMutex m_mutex;  
    58. };  

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

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

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. class WorkerThread : public QThread  
    2. {  
    3.     Q_OBJECT  
    4.   
    5. public:  
    6.     explicit WorkerThread(QObject *parent = 0)  
    7.         : QThread(parent)  
    8.     {  
    9.     }  
    10.   
    11.     ~WorkerThread() {  
    12.         // 请求终止  
    13.         requestInterruption();  
    14.         quit();  
    15.         wait();  
    16.     }  
    17.   
    18. protected:  
    19.     virtual void run() Q_DECL_OVERRIDE {  
    20.         // 是否请求终止  
    21.         while (!isInterruptionRequested())  
    22.         {  
    23.             // 耗时操作  
    24.         }  
    25.     }  
    26. };  

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

    [cpp] view plain copy
     
     在CODE上查看代码片派生到我的代码片
    1. void QThread::requestInterruption()  
    2. {  
    3.     Q_D(QThread);  
    4.     QMutexLocker locker(&d->mutex);  
    5.     if (!d->running || d->finished || d->isInFinish)  
    6.         return;  
    7.     if (this == QCoreApplicationPrivate::theMainThread) {  
    8.         qWarning("QThread::requestInterruption has no effect on the main thread");  
    9.         return;  
    10.     }  
    11.     d->interruptionRequested = true;  
    12. }  
    13.   
    14. bool QThread::isInterruptionRequested() const  
    15. {  
    16.     Q_D(const QThread);  
    17.     QMutexLocker locker(&d->mutex);  
    18.     if (!d->running || d->finished || d->isInFinish)  
    19.         return false;  
    20.     return d->interruptionRequested;  
    21. }  

    ^_^,内部实现居然也用了互斥锁QMutex,这样我们就可以放心地使用了。

    原文链接:http://blog.csdn.net/liang19890820/article/details/52186626

    http://blog.csdn.net/caoshangpa/article/details/62421334

  • 相关阅读:
    牛客IOI周赛17-提高组 卷积 生成函数 多项式求逆 数列通项公式
    6.3 省选模拟赛 Decompose 动态dp 树链剖分 set
    AtCoder Grand Contest 044 A Pay to Win 贪心
    5.29 省选模拟赛 树的染色 dp 最优性优化
    luogu P6097 子集卷积 FST FWT
    CF724C Ray Tracing 扩展欧几里得 平面展开
    5.30 省选模拟赛 方格操作 扫描线 特殊性质
    5.29 省选模拟赛 波波老师 SAM 线段树 单调队列 并查集
    Spring main方法中怎么调用Dao层和Service层的方法
    Bug -- WebService报错(两个类具有相同的 XML 类型名称 "{http://webService.com/}getPriceResponse"。请使用 @XmlType.name 和 @XmlType.namespace 为类分配不同的名称。)
  • 原文地址:https://www.cnblogs.com/findumars/p/6040385.html
Copyright © 2020-2023  润新知