Qt中當(dāng)程序結(jié)束時線程的退出
在Qt程序結(jié)束時應(yīng)該如何退出正在運行的任務(wù)子線程?
因個人經(jīng)驗和能力有限,本文僅供參考,有錯誤或者考慮不完善的地方請各位批評指正。
一、正常情況下如何創(chuàng)建和退出線程
1.繼承QThread,重寫run()函數(shù)
點擊折疊或展開代碼
// 類的定義
class WorkThread1 : public QThread
{
public:
explicit WorkThread1(QObject *parent = 0);
signals:
// 線程運行完成
void workDone();
public slots:
// 停止線程
void stopThread();
protected:
// 重寫run()函數(shù)
virtual void run() override;
private:
// 是否停止運行
bool isStop = false;
};
WorkThread1::WorkThread1(QObject *parent) : QThread(parent)
{
}
void WorkThread1::run()
{
for (int i = 0; i < 10; ++i) {
if (isStop) {
return ;
}
// 執(zhí)行耗時操作
// TODO
}
}
void WorkThread1::stopThread()
{
isStop = true;
}
// 外部調(diào)用
// 兩個按鈕,分別開啟和停止線程
QPushButton *start = new QPushButton(this);
QPushButton *stop = new QPushButton(this);
WorkThread1 *thread1 = new WorkThread1(this);
connect(start, &QPushButton::clicked, [=](){
thread1->start();
});
connect(stop, &QPushButton::clicked, [=](){
thread1->stopThread();
thread1->wait();
});
2.Qt官方推薦做法,使用moveToThread函數(shù)
點擊折疊或展開代碼
// 任務(wù)類
class Worker : public QObject
{
Q_OBJECT
public slots:
void doWork(const QString ¶meter) {
QString result;
// 耗時操作
// TODO
emit resultReady(result);
}
signals:
void resultReady(const QString &result);
};
// 外部調(diào)用
class Controller : public QObject
{
Q_OBJECT
// 使用智能指針列表
QList<QPointer<QThread>> workerThreadList;
public:
Controller() {
Worker *worker = new Worker;
QPointer<QThread> workerThread = new QThread;
// 加入管理列表
workerThreadList.append(workerThread);
// 將任務(wù)類移入線程
worker->moveToThread(workerThread);
// 連接信號
connect(workerThread, &QThread::finished, worker, &QObject::deleteLater);
connect(workerThread, &QThread::finished, workerThread, &QThread::deleteLater);
connect(this, &Controller::operate, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &Controller::handleResults);
// 啟動線程
workerThread->start();
}
~Controller() {
for (QPointer<QThread> thread : qAsConst(workerThreadList)) {
if (! thread.isNull) {
thread.quit();
thread.wait();
}
}
}
public slots:
void handleResults(const QString &);
signals:
void operate(const QString &);
};
3. 其他方法快速創(chuàng)建線程任務(wù)
點擊折疊或展開代碼
// QThread靜態(tài)函數(shù)create
QThread *thread = QThread::create([=](){
// 耗時操作
// TODO
});
// QtConcurrent模塊,使用時需要在pro文件中引入QT += concurrent,使用頭文件#include <QtConcurrent>,可在幫助文檔中搜索 Concurrent Run
QtConcurrent::run([=](){
// 耗時操作
// TODO
});
// QRunnable 除非有線程池的需求,否則個人不推薦使用。因為使用起來需要繼承和重寫,而且使用信號和槽比較麻煩
MyRunnable *r = new MyRunnable; // 需要繼承自QRunnable,重寫run函數(shù)
QThreadPool threadpool;
threadpool.setMaxThreadCount(1);
threadpool.start(r);
二、程序退出時如何退出正在運行的線程
- 在耗時的循環(huán)操作中添加標(biāo)志位的判斷,在退出時將標(biāo)志位設(shè)置成退出標(biāo)志位,比如上面的WorkThread1 示例中重寫的run函數(shù)中的處理方式,如果層級太多,該用全局變量就使用全局變量
- 翻閱幫助文檔發(fā)現(xiàn)QThread有以下兩個函數(shù),和標(biāo)志位差不多的用法
點擊折疊或展開代碼
// 請求中斷線程
void QThread::requestInterruption()
// 是否請求了中斷線程
bool QThread::isInterruptionRequested() const
// 使用方法
// 當(dāng)中斷線程時,外部調(diào)用
thread->requestInterruption();
// 在線程的任務(wù)循環(huán)中添加判斷
void long_task() {
forever {
if ( QThread::currentThread()->isInterruptionRequested() ) {
return;
}
}
}
三、處理異常情況程序退出后卻仍然在后臺掛起
在接收到終止信號時,需要退出線程并且退出主線程,可能會因為子線程耗時太長導(dǎo)致程序在后臺掛起的情況(可能是這個原因),那么就需要先退出子線程,再退出主線程
點擊折疊或展開代碼
// 管理線程列表
QList<QPointer<QThread>> mThreadList;
// 創(chuàng)建智能指針
QPointer<QThread> thread = new QThread;
MyObject *obj = new MyObject;
// 移入線程
obj->moveToThread(thread);
// 連接信號和槽
connect(thread, &QThread::finished, obj, &MyObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// 其他可能的線程正常退出的情況,自定義結(jié)束信號finished
connect(obj, &MyObject::finished, [=](){
// TODO
if (! thread.isNull) {
thread->quit();
thread->wait();
}
});
// 程序結(jié)束時,循環(huán)阻塞,線程全退出后主線程退出
while (mThreadList.size() > 0) {
QPointer<QThread> thread = mThreadList.first();
if ( ! thread.isNull()) {
thread->quit();
thread->wait();
}
mThreadList.pop_front();
}
qApp.exit();

浙公網(wǎng)安備 33010602011771號