2012-02-11 66 views
2

我有我一直在嘗試用一些粗糙的代碼:如何停止運行阻塞永久循環的QThread?

someserver.cpp(一GUI)

#include "server.h" 
#include "ui_server.h" 

Server::Server(QWidget *parent) : 
    QMainWindow(parent), 
    ui(new Ui::Server) 
{ 
    ui->setupUi(this); 
} 

Server::~Server() 
{ 
    delete ui; 
} 

void Server::onBtnStartClicked() 
{ 
    QThread worker; 
    worker.start(); // Start worker thread that goes into an infinite loop with a blocking call 
} 

void Server::onBtnExitClicked() 
{ 
    // How do I cleanly stop worker from running? 
    QApplication::quit(); 
} 

worker.cpp

#include "worker.h" 

Worker::Worker(QObject *parent) : 
    QThread(parent) 
{ 
} 

void Worker::run() 
{ 
    for (;;) 
    { 
     // a blocking IO call here like pipe, or msgrcv 
     // process data received 
    } 
} 

由於工作線程在運行永遠循環使用阻塞IO調用,我將如何構造它以便在GUI線程中按下停止按鈕時,工作線程乾淨地停止?

回答

3

您當然可以在for循環中輸入一個布爾值,Worker::run()每次迭代都檢查一次,它打破== true,並且由gui Stop按鈕設置。當然,這在執行被阻止時不會退出線程。

可能更好的是擺脫for循環,並使用Qt的信號和插槽來設置回撥函數,連接到像QIODevice::readyRead()這樣的信號。只有在socket/pipe中有信息時纔會調用它們。任何其他時間你都可以退出QThread::exit()。您還需要在某個時刻致電QThread::exec()以使事件循環繼續。

+0

的問題是阻塞調用是System V消息隊列,所以我不認爲我可以使用像QIODevice這樣的設置回調函數。 – 2012-02-11 21:29:18

+0

@CodingDistrict我對這些並不熟悉,但肯定有某種回調裝置用於何時準備好消息?然而,這工作,我會認爲你可以繼承'QLocalSocket'併爲其打包。 – 2012-02-11 21:33:34

0

首先,你應該設置父爲Worker使它的另一QObject孩子,進行清潔時,應用程序是做了正確的方式,並在堆創建它。即動態方式。

,做你想做的最簡單的方法是定義插槽將設置Worker對象的布爾成員,並檢查它在每個週期打破死循環。不要忘記將此插槽連接到正確的信號。

所以你可以把在Server的構造沿着線

QThread * worker = new QThread (this); 
connect (this, SIGNAL(terminator()), worker, SLOT(terminate())); 

Server類聲明:

signals: 
    terminator(); 

Worker類:

private: 
    bool terminate_; // don't forget initialize it to false 
public slots: 
    void terminate(){terminate_ = true;} 

,並在週期:

if (terminate_) {/* .. do exit things...*/; return; 

之後從服務器發出信號terminator()只要你需要。

如果你需要更復雜的管理然後簡單的停止,你可能需要用互斥體來保護你的狀態變量。

0

要停止工作線程,需要添加「停止請求」布爾標誌,該標誌應在每次迭代開始時進行測試。還需要同步來讀取/寫入該布爾標誌的值。

這裏是草案版本:

class Worker : public QThread { 
    public: 
     Worker() : stopRequested(false) {} 

     void requestStop() 
     { 
      QMutexLocker locker(&mutex); 
      stopRequested = true; 
     } 

     void run() 
     { 
      forever 
      { 
       if (stopRequested()) 
        break; 

       // a blocking IO call here like pipe, or msgrcv 
       // process data received 
      } 
     } 

    private: 
     bool stopRequested() 
     { 
      QMutexLocker locker(&mutex); 
      return stopRequested; 
     } 

     QMutex mutex; 
     bool stopRequested; 
}; 
+0

這裏的同步點是什麼?過度同步並不比同步不好。同樣@Matt菲利普斯提到,如果線程在IO操作中被阻塞,這並沒有幫助。 – Lol4t0 2012-02-11 21:11:10

+0

此外,有必要將信號(類似於「停止」)添加到「工人」類並在永久循環後發出。 UI級別類應與信號連接並接收「已停止」通知。 – 2012-02-11 21:19:27

+1

http://developer.qt.nokia.com/doc/qt-4.8/qthread.html#finished – Lol4t0 2012-02-11 21:20:52

0

Here你有你的解釋是問題,我想。當你的應用程序將結束,你應該做這樣的事情:

MyClass::~MyClass() 
{ 
    m_thread->requestStop(); 
    m_thread->wait(); // Force the thread trying to destroy 
         // an object of type MyClass, to wait 
         // the end of the execution of m_thread 
    delete m_thread; 
} 

wait()是您的同步問題的解決方案。你也可以改變requestStop()到:

void requestStop() 
{ 
    mutex.lock(); 
    stopRequested = true; 
    mutex.unlock(); 
    wait(); 
} 

刪除QMutexLocker避免死鎖的情況是很重要的。

1

在無限循環粘貼此代碼,並享受...

QTime dieTime = QTime::currentTime().addMSecs(1); 
while(QTime::currentTime() < dieTime) { 
    QCoreApplication::processEvents(QEventLoop::AllEvents, 1); 
} 

不要忘記

#include <QCoreApplication> 

#include <QTime> 

看你