• 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
  • 相关阅读:
    Android 关于屏幕适配
    android 数据存储之SharePreference 的几种方式
    Android多项目依赖在Eclipse中无法关联源代码的问题解决 Ctril 点不进去的解决方法
    android onIntent 是什么东西
    Android一次退出所有Activity的方法(升级版)
    Android 一次退出所有activity的方法
    android 获取屏幕尺寸
    自定义view(自定义view的时候,三个构造函数各自的作用)
    Android应用自动更新功能的实现!!!
    android实现透明和半透明效果
  • 原文地址:https://www.cnblogs.com/djh5520/p/14754646.html
Copyright © 2020-2023  润新知