2013-03-07 67 views
1

我在讀我應該使用worker對象,並通過moveToThread將其移動到線程,而不是直接從QThread繼承。但我無法找到解決方案如何停止我的對象工人循環。例如,我有測試迴路:QThread - 如何阻止工人?

void CollectionWorker::doWork() 
{ 
    for (int i = 0; i < 10; ++i) { 
     sleep(1); 
     emit ping(i); 
    } 
} 

現在我提出這個對象的線程:

worker->moveToThread(mTh); 

這是工作的罰款。但是當我調用mTh.quit()時,線程正在等待,直到doWork中的循環結束。當我直接從QThread繼承,然後在每個循環中,我可以檢查線程狀態並在完成完成時斷開循環,但不知道如何在工作對象中執行此操作。我可以在工作對象中創建一些標誌並將其從主線程中切換出來嗎?或者,也許我可以找到線程所有者並檢查它的狀態?或者,也許在開始線程之前更好,在工作對象中設置線程指針,然後檢查狀態?什麼是最好的線程安全解決方案?

Regards

回答

2

編輯:對不起,我誤解了你的問題。

有幾種選擇。您可以創建一些標誌,並在處理循環的每次迭代之前檢查標誌是否已設置。或者,如果您在列表/隊列中處理數據,您可能會發信號通知進程終止一個特殊的數據結束元素。

+0

是的我知道,但這不是問題。如果我的循環是「永遠」的話會怎麼樣?我想用類似下面的方式來阻止它: for(int i = 0; i <10; ++ i)if() return; sleep(1); 發出ping(i); } – Dibo 2013-03-07 11:14:29

+1

該標誌最有意義。'while(stillDoingWork && notStopped)' – Anthony 2013-03-07 11:57:05

+0

是否在主線程(例如,來自按鈕clik)的線程安全中設置工作者標誌?只有主線程寫入此標誌,工作人員纔會讀取 – Dibo 2013-03-07 12:06:11

0

Qt中的慣用的方式來破壞輔助線程使用信號/槽接口:

爲了這個工作CollectionWorker必須從一個QObject類繼承和聲明Q_OBJECT宏。

4

如果有任何運行,調用線程對象的quit()或exit()將簡單地結束線程的事件循環。但正如您正確指出的那樣,原始問題仍然是一樣的。如果工作者函數已經被事件循環執行並且是具有永久構造的長時間運行的函數呢?對quit()或exit()的調用只需等待worker函數返回即可。

除了向調用者提供公共函數外,還可以建議兩種方法,這些方法會改變內部標誌。

  • 給你的工人階級一個終止信號和插槽。如下所示。

    signals: 
     void signalTermination(); 
    public slots: 
     void setTerminationFlag(); 
    private: 
     QMutex mutex; 
     bool terminationRequested; 

你的插槽看起來像什麼。

void setTerminationFlag() 
{ 
    QMutexLocker locker(&mutex); 
    terminationRequested = true; 
} 

然後,您可以在永久循環的每次迭代中檢查doWork函數中的變量。

mutex.lock(); 
if(terminationRequested) 
{ 
    //break from loop and effectively doWork function 
} 
mutex.unlock(); 

對使用​​,而不是普通的成員函數的信號和槽的原因是,如果你的工人函數是做同步的代碼塊中需要長時間運行的任務,公共職能將保持封鎖,直到它得到的訪問同步對象。如果公共終止方法的調用線程是UI線程,這可能會產生不利影響。

  • 另一個乾淨和簡單的方法,如果你使用的是Qt 5.2起

使用requestInterruption()的QThread的方法。此方法在線程對象中設置諮詢標誌,您可以在doWork()函數中通過isInterruptionRequested()函數調用來檢查該標誌。請參閱QThread::isInterruptionRequested文檔中給出的以下代碼片段。

void long_task() { 
    forever { 
     if (QThread::currentThread()->isInterruptionRequested()) { 
      return; 
     } 
    } 
} 

您也可以直接調用quit()來結束線程的事件循環。

+0

你的第一個建議似乎不適合我。雖然我們使用的是信號,但只有在長循環結束後纔會執行'setTerminationFlag()'函數,就像您爲成員函數所描述的那樣。第二個選項'isInterruptionRequested()'會工作正常,如果不是因爲你以後想做類似的工作而不能重置這個標誌。 – 2016-08-17 08:42:16

0

我有和Dibo一樣的問題。我在我的doWork()函數中運行了一個很長的計算循環,並且需要能夠從我的主線程中停止它。

查迪克羅伯特的答案沒有爲我做的伎倆。第一個建議的表現與他爲成員函數描述的一樣。也就是說,雖然我們正在使用信號和插槽,但是從主線程中發出的信號調用setTerminationFlag插槽,並正確連接到它,只有在循環結束後纔會執行。但是,也許我在執行方面做了錯誤。如果它確實應該工作,請讓我知道,因爲它似乎是交易斷路器。

他的第二種替代方法是使用QThread::currentThread()->isInterruptionRequested()來告訴線程停止,如果不是因爲你不能重置這個標誌,而你打算重新使用線程和worker來執行一些類似的處理密集循環如果不一樣的話。當然,你可以通過停止和啓動線程來完成它,但是我不確定它是否不會有像清空執行隊列那樣的不利影響。這個問題實際上帳爲bug report和蒂亞戈Macieira(Qt的主要開發者)提到那裏,如果我可以引述他:

的requestInterruption功能的目的是完成線程。

這使得requestInterruption不適合作業,因爲它僅在線程開始和結束時重置。

一個解決方案,我發現,這似乎不是所有的清潔對我來說,是包括QCoreApplication在工人階級和時間打電話QCoreApplicaton::processEvents()時間,來處理Chadick Robbert的第一個建議那些排隊的信號或更新工作者對線程之間共享標誌變量的類感知。

因爲你的循環中調用QCoreApplicaton::processEvents()可以減緩它極大地我做的是一樣的東西:

for(unsigned long int i=0; i<800000000 && !*stop; i++){ 
    f += (double)i * .001; // dummy calculation 
    if(i%10000000 == 0) // Call it only from time to time 
     QCoreApplication::processEvents(); // update *stop according to main Thread 
} 

正如你所看到的,用此溶液循環只會在「千萬」整數倍打破這可能不適合某些使用情況。

如果有人知道這個殺手解決方案,我很樂意聽到。

+0

是的。第一種選擇應該是這樣工作的。請注意我的回答中的陳述「然後你可以在**每次迭代**中永久循環中檢查doWork函數中的變量。」 – 2016-08-18 15:08:27

+0

這裏的要點是,如果我們想要優雅地完成線程,您希望線程至少完成其當前循環的執行。除非您的代碼擁有消息泵,並且在執行每個語句之前檢查它,否則沒有更簡潔的方法可以在循環的當前行中獲取中斷。正如你在你自己的答案中指出的,即使你的解決方案只能工作在10000000的倍數,但如果我想結束線程數12345678. – 2016-08-18 15:08:50

+0

因此,如果它是一個非常長的循環,請在步驟中創建邏輯檢查點,然後檢查該標誌或調用函數'isInturruptionRequested()'。再次,我只是建議更乾淨的方式來結束這個線程。 – 2016-08-18 15:14:34