在子线程中维护一个任务队列,排队执行主线程中添加的耗时/后台任务。
设计的任务类如下:
#ifndef TASK_H #define TASK_H #include <QObject> // Demo是为了方便,将接口类和具体类写在了一起 // 任务接口类 class CTask { public: virtual ~CTask() {} virtual int doWork() = 0; virtual QString message() = 0; }; // 结束任务,不做实质性操作,一般仅用作结束线程的标志 class CEndTask : public CTask { public: int doWork() override; QString message() override; }; // 实际的任务实现类 class CMyTask : public CTask { public: CMyTask(int taskId); int doWork() override; QString message() override; private: int id; // 任务id }; #endif // TASK_H
#include "task.h" #include <QThread> #include <QDebug> #include <QDateTime> CMyTask::CMyTask(int taskId) : id(taskId) { } int CMyTask::doWork() { // 模拟耗时的任务,doWork里面不要执行任何操作GUI控件的操作 for (int i = 0; i < 10; ++i) { QThread::msleep(1000); qDebug() << "[CMyTask:" << id << "]run in thread:" << QThread::currentThreadId() << " >>> " << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); } return 0; } QString CMyTask::message() { return QString("MyTask"); } int CEndTask::doWork() { qDebug() << "[CEndTask Thread:" << QThread::currentThreadId() << "] >>> " << QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss zzz"); return 0; } QString CEndTask::message() { return QString("CEndTask"); }
线程类:
#ifndef TASKTHREAD_H #define TASKTHREAD_H #include <QThread> #include <QQueue> #include <QWaitCondition> #include <QMutex> class CTask; class CEndTask; class TaskThread : public QThread { Q_OBJECT public: explicit TaskThread(QObject *parent = nullptr); ~TaskThread(); void addTask(CTask *task); // 添加任务的接口 void stopRun(); // 停止线程执行接口 protected: void run() override; // 定义一些与主线程通信的信号 signals: void taskStart(const QString &message); void allTaskDone(); private: QQueue<CTask*> taskQueue; // 任务队列 QWaitCondition taskAdded; // 任务等待条件 QMutex mutex; // CTask *EndTask; // 结束任务 }; #endif // TASKTHREAD_H
#include <QMutexLocker> #include <QDebug> #include "task.h" #include "taskthread.h" TaskThread::TaskThread(QObject *parent): QThread(parent), EndTask(new CEndTask) { start(); // 构造时即开启,也可以不立马开启线程 } TaskThread::~TaskThread() { { QMutexLocker locker(&mutex); // 清空任务队列 while (!taskQueue.isEmpty()) { delete taskQueue.dequeue(); } } if (this->isRunning()) { stopRun();// stop thread } wait(); delete EndTask; } void TaskThread::addTask(CTask *task) { // 必须加锁访问任务队列,防止并发访问发生冲突 QMutexLocker locker(&mutex); taskQueue.enqueue(task); // 添加任务 taskAdded.wakeOne(); } void TaskThread::stopRun() { QMutexLocker locker(&mutex); taskQueue.enqueue(this->EndTask); // 添加结束任务 taskAdded.wakeOne(); } void TaskThread::run() { CTask *task = nullptr; qDebug() << "Task Thread ID:" << QThread::currentThreadId(); forever { { QMutexLocker locker(&mutex); if (taskQueue.isEmpty()) { // 任务队列为空,等待有任务时唤醒 taskAdded.wait(&mutex); } // 获取一个任务 task = taskQueue.dequeue(); // 判断是否是退出任务 if (task == EndTask) { task->doWork(); // 执行结束任务 break; // 跳出循环,结束线程 } } if (task) { // 发送任务开始执行信号 emit taskStart(task->message()); // 执行任务 task->doWork(); // 执行完成将其删除 delete task; task = nullptr; } // 检查任务是否全部完成 { QMutexLocker locker(&mutex); if (taskQueue.isEmpty()) qDebug() << "All Task Is Done Now!"; emit allTaskDone(); } } qDebug() << "Thread Run End!"; }
使用Demo
#ifndef MAINWINDOW_H #define MAINWINDOW_H #include <QMainWindow> class TaskThread; namespace Ui { class MainWindow; } class MainWindow : public QMainWindow { Q_OBJECT public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); private slots: void on_btnAddTask_clicked(); void on_actionStop_triggered(); private: Ui::MainWindow *ui; TaskThread* thd = nullptr; }; #endif // MAINWINDOW_H
#include "mainwindow.h" #include <QDebug> #include "ui_mainwindow.h" #include "task.h" #include "taskthread.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); qDebug() << "Main Thread ID:" << QThread::currentThreadId(); thd = new TaskThread(this); // 可以关联线程的信号 } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_btnAddTask_clicked() { static int id = 0; if (thd->isRunning()) thd->addTask(new CMyTask(id++)); } void MainWindow::on_actionStop_triggered() { if (thd->isRunning()) thd->stopRun(); }
测试效果如下:(点击3下任务添加按钮,添加3个任务,停止Action用于终止线程执行)
11:28:15: Starting D:DesktopCodeProQtThreadAppDebugdebugThreadApp... Main Thread ID: 0x35fc Task Thread ID: 0x3b2c [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:26 763" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:27 775" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:28 777" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:29 778" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:30 779" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:31 780" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:32 781" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:33 782" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:34 783" [CMyTask: 0 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:35 784" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:36 785" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:37 786" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:38 787" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:39 788" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:40 789" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:41 791" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:42 792" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:43 792" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:44 793" [CMyTask: 1 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:45 793" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:46 794" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:47 795" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:48 796" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:49 797" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:50 799" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:51 800" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:52 801" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:53 802" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:54 803" [CMyTask: 2 ]run in thread: 0x3b2c >>> "2021-05-11 11:28:55 803" All Task Is Done Now! [CEndTask Thread: 0x3b2c ] >>> "2021-05-11 11:29:00 306" Thread Run End! 11:29:07:D:/Desktop/CodePro/Qt/ThreadApp/Debug/debug/ThreadApp exited with code 0