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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM