Qt设计子线程执行任务队列

  在子线程中维护一个任务队列,排队执行主线程中添加的耗时/后台任务。

  设计的任务类如下:

#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
原文地址:https://www.cnblogs.com/djh5520/p/14754646.html