如果想要停止執行緒,QThread有個terminate()方法,但是這個方法並不建議使用,因為執行緒會直接停止正在進行的程式流程,無論現在是在流程的哪個位置,這會使得一些資源的善後工作無法完成,或因程式流程嘎然中止而導致不可預期的程式錯誤。
一個執行緒要停止,基本上就是執行完run()方法,讓它進入完成(Finished),簡單的說,如果您想要停止一個執行緒的執行,就要提供一個方式讓執行緒可以執行完run(),而這也是您自行實作執行緒停止的基本概念。
如果執行緒的run()方法中執行的是一個重複執行的迴圈,您可以提供一個flag來控制迴圈是否執行,藉此讓迴圈有可能終止、執行緒可以離開 run()方法以終止執行緒,以下面的實例來說,您提供一個bool的stopped變數:
- MessageThread.h
#ifndef MESSAGETHREAD_H
#define MESSAGETHREAD_H
#include <QThread>
class MessageThread : public QThread {
Q_OBJECT
public:
MessageThread();
void setMessage(const QString &message);
void stop();
protected:
void run();
private:
QString msg;
bool stopped;
};
#endif
在這個類別中,您可以藉由setMessage()設定要顯示的訊息文字,在run()方法中,while迴圈由stopped變數判斷是否繼續迴圈:
- MessageThread.cpp
#include "MessageThread.h"
#include <iostream>
using namespace std;
MessageThread::MessageThread() {
this->stopped = false;
}
void MessageThread::setMessage(const QString &msg) {
this->msg = msg;
}
void MessageThread::stop() {
stopped = true;
}
void MessageThread::run() {
while (!stopped) {
cerr << qPrintable(msg) << endl;
QThread::sleep(1);
}
}
若要停止執行緒,可以呼叫stop()方法,這會使得stopped變數設為true,而使得while迴圈可以結束,從而可以讓執行緒執行完run()而進入完成。
要判斷執行緒是否正在執行,可以使用QThread的isRunning()方法,要判斷執行緒是否完成,可以使用QThread的isFinished(),以下可以寫個簡單的程式來使用以上的MessageThread:
- DemoDialog.h
#ifndef DEMODIALOG_H
#define DEMODIALOG_H
#include "MessageThread.h"
#include <QDialog>
class QWidget;
class QPushButton;
class QCloseEvent;
class DemoDialog : public QDialog {
Q_OBJECT
public:
DemoDialog(QWidget *parent = 0);
public slots:
void startOrStop();
protected:
void closeEvent(QCloseEvent *event);
private:
QPushButton *btn;
MessageThread thread;
};
#endif
程式中會有個按鈕,按下後可以啟動另一個執行緒,並設定按鈕文字為「Stop」,若再按下,則會呼叫 MessageThread的stop()停止執行緒,此時設定按鈕文字為「Finished」,並設定按鈕為不可按下,執行緒完成後,再呼叫其 start()方法是沒有作用的:
- DemoDialog.cpp
#include "DemoDialog.h"
#include "MessageThread.h"
#include <QPushButton>
#include <QVBoxLayout>
#include <QCloseEvent>
DemoDialog::DemoDialog(QWidget *parent) : QDialog(parent) {
btn = new QPushButton("Start", this);
QVBoxLayout *layout = new QVBoxLayout;
layout->addWidget(btn);
this->setLayout(layout);
thread.setMessage("message....");
connect(btn, SIGNAL(clicked()), this, SLOT(startOrStop()));
}
void DemoDialog::startOrStop() {
if(thread.isRunning()) {
thread.stop();
btn->setText("Finished");
btn->setEnabled(false);
}
else {
thread.start();
btn->setText("Stop");
}
}
void DemoDialog::closeEvent(QCloseEvent *event) {
thread.stop();
thread.wait();
event->accept();
}
QThread的wait()方法,可以確實的等待執行緒完成,再進行接下來的動作,您也可以指定wait()的時間,在時間到時,無論如何就進行接下來的動作。
可以撰寫以下的簡單程式來使用DemoDialog:
- main.cpp
#include <QApplication>
#include "DemoDialog.h"
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
DemoDialog *demoDialog = new DemoDialog;
demoDialog->setWindowTitle("Thread Demo");
demoDialog->resize(200, 50);
demoDialog->show();
return app.exec();
}
有關於執行緒的終止,還可以參考 Two-phase Termination 模式。