Qt/C++ 线程池TaskPool与 Worker 框架实践

前言

本文介绍一个基于 Qt/C++ 的任务池与 Worker 框架:应用场景、使用方式、架构设计思路、关键代码,以及构建与测试方法。

为什么需要 TaskPool + Worker

  • 统一并发入口:将不同类型任务(CPU/IO/网络/图形/定时)统一提交到任务池,按优先级调度执行。
  • 可观测与可控:内置任务状态、生命周期信号、进度与消息回传、取消与超时控制。
  • 跨平台与高可用:基于 QtCore/QtNetwork/QtGui,适配 Windows/Linux/macOS,线程安全无泄漏。

构建与运行

  • 本文代码使用 VS2019 + Qt 5.15.2 + CMake构建。

本文demo下载

应用场景

  • CPU 密集型:分段素数计算或其它数值计算(示例:examples/PrimeExample/main.cpp)。
  • IO 密集型:目录文件批处理、日志统计(示例:examples/IOExample/main.cpp)。
  • 网络并发:批量 HTTP 请求、抓取数据(示例:examples/NetworkExample/main.cpp)。
  • 图形处理:像素级图像处理(灰度、滤波)(示例:examples/ImageExample/main.cpp)。
  • 定时调度:周期性任务提交(示例:examples/SchedulerExample/main.cpp)。

框架设计

  • 核心角色:

    • TaskPool:管理线程池与优先级任务队列,提供提交/取消/状态查询。
    • Worker:任务执行基类,封装消息与进度回调、暂停/恢复/停止控制,生命周期与错误信号。
    • CancellationToken:协作式取消与超时令牌,任务通过轮询令牌状态决定退出。
    • TaskRunnable:将提交的函数包装为 QRunnable,桥接到 QThreadPool;驱动 Worker 的生命周期信号与状态上报。
    • TaskHandle:任务提交返回的句柄,用于取消与线程安全状态查询。
  • 状态与信号:

    • TaskStatus(Pending/Running/Completed/Cancelled/Failed/Timeout)作为统一状态枚举。
    • TaskPool::taskStateChanged(QUuid, TaskStatus) 供外部监听;Worker 发射 started/finished/progress/message/error/cancelled/timeout

目录结构:

使用方式

  1. 定义一个 Worker 子类,覆盖 execute(CancellationToken&),在执行中检查取消/超时/暂停/停止。
  2. 创建 TaskPool,设置线程数与连接状态信号。
  3. 调用 submitTask 提交任务函数(可绑定 Worker),得到 TaskHandle;需要时调用 cancel() 取消。
  4. 在主线程中通过信号槽获取生命周期与进度。

示例(合并自 IO/网络场景):

cpp 复制代码
// 创建任务池并设置并发线程数
TaskPool pool;
pool.setMaxThreads(QThread::idealThreadCount());

// 任务状态监听(主线程)
QObject::connect(&pool, &TaskPool::taskStateChanged,
                 [](const QUuid &id, TaskStatus s) {
                   Q_UNUSED(id);
                   // 依据状态做 UI/业务更新
                 });

// 自定义 Worker 子类(可参考 examples)
class MyWorker : public Worker {
    Q_OBJECT
public:
    void execute(CancellationToken &token) override {
        // 检查取消/超时/暂停/停止
        if (token.isCancelled() || token.isTimeout() || isStopped()) {
            emit cancelled();
            return;
        }
        // 执行具体任务,定期发射进度
        emit progress(50);
    }
};

// 提交任务
auto w = new MyWorker();
TaskHandle h = pool.submitTask([w](CancellationToken &t){ w->execute(t); }, /*priority*/1, /*timeoutMs*/5000, w);

// 可选:取消任务
h.cancel();

关键代码

  • 优先级调度(src/TaskPool.cpp:44-src/TaskPool.cpp:65
cpp 复制代码
void TaskPool::schedule() {
    QMutexLocker l(&m_mutex);
    while (!m_queue.empty() && m_pool.activeThreadCount() < m_pool.maxThreadCount()) {
        TaskItem item = m_queue.top();
        m_queue.pop();

        auto tokIt = m_tokens.find(item.id);
        std::shared_ptr<CancellationToken> token;
        if (tokIt != m_tokens.end()) {
            token = tokIt.value();
        }

        TaskRunnable* r = new TaskRunnable(item.id, item.func, token, item.timeoutMs, item.worker);
        connect(r,
                &TaskRunnable::stateChanged,
                this,
                &TaskPool::onRunnableState,
                Qt::QueuedConnection);
        m_pool.start(r, item.priority);
    }
}

解释:用 std::priority_queue 实现优先级任务队列,按线程池剩余容量取出并投递。每个任务通过 TaskRunnable 上报状态。

  • Worker 暂停/恢复与消息队列(src/Worker.cpp:33-src/Worker.cpp:70
cpp 复制代码
void Worker::postMessage(const QVariant &msg) {
    QMutexLocker locker(&m_mutex);
    m_messages.enqueue(msg);
}

void Worker::waitIfPaused() {
    if (isPaused()) {
        QMutexLocker locker(&m_mutex);
        while (isPaused() && !isStopped()) {
            m_pauseCond.wait(&m_mutex, 50);
        }
    }
}

bool Worker::takeMessage(QVariant &out) {
    QMutexLocker locker(&m_mutex);
    if (m_messages.isEmpty()) {
        return false;
    }
    out = m_messages.dequeue();
    return true;
}

解释:提供线程安全的消息收发;暂停时通过条件变量阻塞等待,恢复/停止时唤醒。

  • 任务执行包装与生命周期(src/TaskRunnable.cpp:19-src/TaskRunnable.cpp:56
cpp 复制代码
void TaskRunnable::run() {
    emit stateChanged(m_id, TaskStatus::Running);
    if (m_token && m_timeoutMs > 0) {
        m_token->setTimeoutMs(m_timeoutMs);
    }
    if (m_worker) emit m_worker->started();
    try {
        if (m_token) {
            m_func(*m_token);
        } else {
            CancellationToken t;
            m_func(t);
        }
        if (m_token && m_token->isCancelled()) {
            emit stateChanged(m_id, TaskStatus::Cancelled);
        } else if (m_token && m_token->isTimeout()) {
            if (m_worker) emit m_worker->timeout();
            emit stateChanged(m_id, TaskStatus::Timeout);
        } else {
            if (m_worker) emit m_worker->finished();
            emit stateChanged(m_id, TaskStatus::Completed);
        }
    } catch (const std::exception& e) {
        if (m_worker) emit m_worker->error(QString::fromUtf8(e.what()));
        emit stateChanged(m_id, TaskStatus::Failed);
    } catch (...) {
        if (m_worker) emit m_worker->error(QString::fromUtf8("Unknown"));
        emit stateChanged(m_id, TaskStatus::Failed);
    }
    if (m_worker) {
        m_worker->deleteLater();
    }
}

解释:在 QRunnable::run() 中设置超时、调用任务函数、分发生命周期事件、捕获异常并标记失败,最后释放 Worker。

部分文件完整代码

TaskPool.cpp

cpp 复制代码
#include "TaskPool.h"
#include <QMetaObject>

using namespace tpw;

/* 构造:设置线程到期超时,保留线程池可复用性 */
TaskPool::TaskPool(QObject* parent)
    : QObject(parent) {
    m_pool.setExpiryTimeout(30000);
}

/* 设置最小线程数:提高最大线程数并禁用过期,保持常驻 */
void TaskPool::setMinThreads(int n) {
    m_minThreads = n;
    if (m_pool.maxThreadCount() < n) {
        m_pool.setMaxThreadCount(n);
    }
    m_pool.setExpiryTimeout(-1);
}

void TaskPool::setMaxThreads(int n) { m_pool.setMaxThreadCount(n); }

/* 提交任务:入队并尝试调度 */
TaskHandle TaskPool::submitTask(const std::function<void(CancellationToken&)>& func,
                                int priority,
                                int timeoutMs,
                                Worker* worker) {
    TaskHandle h;
    TaskItem item{priority, h.id(), func, timeoutMs, worker};
    {
        QMutexLocker l(&m_mutex);
        m_queue.push(item);
        m_state[h.id()] = TaskStatus::Pending;
        auto t = std::make_shared<CancellationToken>();
        m_tokens[h.id()] = t;
    }
    h.setCancelFunc([this, id = h.id()]() { cancelTask(id); });
    h.setStatusFunc([this, id = h.id()]() { return status(id); });
    schedule();
    return h;
}

/* 调度器:按优先级从队列取出并投递到 QThreadPool */
void TaskPool::schedule() {
    QMutexLocker l(&m_mutex);
    while (!m_queue.empty() && m_pool.activeThreadCount() < m_pool.maxThreadCount()) {
        TaskItem item = m_queue.top();
        m_queue.pop();

        auto tokIt = m_tokens.find(item.id);
        std::shared_ptr<CancellationToken> token;
        if (tokIt != m_tokens.end()) {
            token = tokIt.value();
        }

        TaskRunnable* r = new TaskRunnable(item.id, item.func, token, item.timeoutMs, item.worker);
        connect(r,
                &TaskRunnable::stateChanged,
                this,
                &TaskPool::onRunnableState,
                Qt::QueuedConnection);
        /* 将任务投递至线程池,使用项目内优先级作为 thread priority */
        m_pool.start(r, item.priority);
    }
}

/* 取消任务:触发令牌取消 */
void TaskPool::cancelTask(const QUuid &id) {
    QMutexLocker locker(&m_mutex);
    auto it = m_tokens.find(id);
    if (it != m_tokens.end()) {
        it.value()->cancel();
    }
}

/* 查询状态:若未知则视为 Pending */
TaskStatus TaskPool::status(const QUuid &id) const {
    QMutexLocker locker(&m_mutex);
    auto it = m_state.find(id);
    if (it != m_state.end()) {
        return it.value();
    }
    return TaskStatus::Pending;
}

/* 收到 Runnable 状态:更新内部状态并转发信号 */
void TaskPool::onRunnableState(const QUuid& id, TaskStatus s) {
    {
        QMutexLocker l(&m_mutex);
        m_state[id] = s;
        if (s == TaskStatus::Completed ||
            s == TaskStatus::Cancelled ||
            s == TaskStatus::Failed ||
            s == TaskStatus::Timeout) {
            m_tokens.remove(id);
        }
    }
    emit taskStateChanged(id, s);
}

TaskRunnable.cpp

cpp 复制代码
#include "TaskRunnable.h"

using namespace tpw;

/* 构造:绑定ID/函数/令牌/超时与关联的Worker */
TaskRunnable::TaskRunnable(const QUuid &id,
                           Func func,
                           std::shared_ptr<CancellationToken> token,
                           int timeoutMs,
                           Worker *worker)
    : m_id(id),
      m_func(std::move(func)),
      m_timeoutMs(timeoutMs),
      m_token(std::move(token)),
      m_worker(worker) {
    setAutoDelete(true);
}

void TaskRunnable::run() {
    /* 进入运行态 */
    emit stateChanged(m_id, TaskStatus::Running);
    /* 配置超时限制(若启用) */
    if (m_token && m_timeoutMs > 0) {
        m_token->setTimeoutMs(m_timeoutMs);
    }
    /* Worker 生命周期:开始 */
    if (m_worker) emit m_worker->started();
    try {
        /* 执行任务主体 */
        if (m_token) {
            m_func(*m_token);
        } else {
            CancellationToken t;
            m_func(t);
        }
        /* 根据令牌状态归档结果 */
        if (m_token && m_token->isCancelled()) {
            emit stateChanged(m_id, TaskStatus::Cancelled);
        } else if (m_token && m_token->isTimeout()) {
            if (m_worker) emit m_worker->timeout();
            emit stateChanged(m_id, TaskStatus::Timeout);
        } else {
            if (m_worker) emit m_worker->finished();
            emit stateChanged(m_id, TaskStatus::Completed);
        }
    } catch (const std::exception& e) {
        if (m_worker) emit m_worker->error(QString::fromUtf8(e.what()));
        emit stateChanged(m_id, TaskStatus::Failed);
    } catch (...) {
        if (m_worker) emit m_worker->error(QString::fromUtf8("Unknown"));
        emit stateChanged(m_id, TaskStatus::Failed);
    }
    /* 释放 Worker */
    if (m_worker) {
        m_worker->deleteLater();
    }
}

Worker.cpp

cpp 复制代码
#include "Worker.h"

using namespace tpw;

/* 基类构/析 */
Worker::Worker(QObject *parent)
    : QObject(parent) {}

Worker::~Worker()
{}

/* 暂停:置位并不唤醒等待 */
void Worker::pause()
{
    m_paused.storeRelease(1);
}

/* 恢复:清除暂停并唤醒等待线程 */
void Worker::resume()
{
    m_paused.storeRelease(0);
    m_pauseCond.wakeAll();
}

/* 停止:置位并唤醒所有等待线程 */
void Worker::stop()
{
    m_stopped.storeRelease(1);
    m_pauseCond.wakeAll();
}

/* 线程安全地投递一条消息到队列 */
void Worker::postMessage(const QVariant &msg)
{
    QMutexLocker locker(&m_mutex);
    m_messages.enqueue(msg);
}

/* 状态查询:原子读取 */
bool Worker::isPaused() const
{
    return m_paused.loadAcquire() == 1;
}

bool Worker::isStopped() const
{
    return m_stopped.loadAcquire() == 1;
}

/* 在暂停状态下等待,周期性唤醒以便响应停止 */
void Worker::waitIfPaused()
{
    if (isPaused()) {
        QMutexLocker locker(&m_mutex);
        while (isPaused() && !isStopped()) {
            m_pauseCond.wait(&m_mutex, 50);
        }
    }
}

/* 从消息队列取出一条消息(若存在) */
bool Worker::takeMessage(QVariant &out)
{
    QMutexLocker locker(&m_mutex);
    if (m_messages.isEmpty()) {
        return false;
    }
    out = m_messages.dequeue();
    return true;
}
使用演示demo
cpp 复制代码
#include <QCoreApplication>
#include <QDir>
#include <QFile>
#include <QTextStream>
#include <QDebug>
#include "TaskPool.h"
#include "Worker.h"
#include <QObject>

using namespace tpw;
/*
 * 示例:IO 密集型批处理
 * - 遍历当前目录文件并统计行数;
 * - 展示进度回调与暂停/恢复控制。
 */

class IOWorker : public Worker {
    Q_OBJECT
public:
    IOWorker(QString path) : m_path(std::move(path)) {}
    void execute(CancellationToken &token) override {
        QDir d(m_path);
        auto files = d.entryInfoList(QDir::Files | QDir::NoDotAndDotDot);
        int processed = 0;
        for (auto& fi : files) {
            if (token.isCancelled() || isStopped()) { emit cancelled(); return; }
            QFile f(fi.absoluteFilePath());
            if (f.open(QIODevice::ReadOnly)) {
                QTextStream ts(&f);
                int lines = 0;
                while (!ts.atEnd()) { ts.readLine(); ++lines; if (isPaused()) waitIfPaused(); }
                qDebug() << fi.fileName() << lines;
            }
            ++processed;
            emit progress(files.isEmpty() ? 100 : processed * 100 / files.size());
        }
    }
private:
    QString m_path;
};

int main(int argc, char** argv) {
    QCoreApplication app(argc, argv);
    TaskPool pool;
    pool.setMaxThreads(2);
    auto w = new IOWorker(QDir::currentPath());
    auto h = pool.submitTask([w](CancellationToken &t) { w->execute(t); }, 0, 0, w);
    QObject::connect(w, &Worker::progress, [](int v) { qDebug() << v; });
    QObject::connect(&pool, &TaskPool::taskStateChanged, [&](const QUuid& id, TaskStatus s) {
        Q_UNUSED(id);
        if (s == TaskStatus::Completed) app.quit();
    });
    return app.exec();
}

#include "main.moc"

性能与安全考虑

  • 线程池合理配置:setMaxThreads 与(可选)setMinThreads 常驻线程,以降低频繁创建销毁的成本。
  • 任务观察退出:任务应在循环中检查 token.isCancelled() / token.isTimeout() / isStopped();避免强制终止造成资源泄漏。
  • 信号排队与元类型注册:TaskStatus 使用静态元类型注册,跨线程信号能携带该枚举类型。
  • 内存管理:QRunnable::setAutoDelete(true);任务结束后清理令牌映射;Worker 使用 deleteLater() 回到主线程删除。

总结

TaskPoolWorker 为 Qt 项目提供一个统一、可观测、可控的并发执行框架。它将优先级调度、任务取消/超时、生命周期信号与进度回传整合在一起,适用于从 CPU/IO 到网络/图形的各种并发场景。你可以按需扩展 Worker 子类与业务处理逻辑,并通过任务池实现稳定高效的并发控制。

本文demo下载

相关推荐
喵个咪36 分钟前
ASIO 定时器完全指南:类型解析、API 用法与实战示例
c++·后端
共享家95271 小时前
QT-界面优化(下)
开发语言·数据库·qt
2739920291 小时前
生成二维码 QRCode (QT)
开发语言·qt
火山灿火山1 小时前
初识Qt(使用不同中方式创建helloworld)
开发语言·qt
phdsky1 小时前
【设计模式】抽象工厂模式
c++·设计模式·抽象工厂模式
雾岛听蓝2 小时前
C++ 入门核心知识点(从 C 过渡到 C++ 基础)
开发语言·c++·经验分享·visual studio
xlq223223 小时前
19.模版进阶(上)
c++
yuuki2332333 小时前
【C++】初识C++基础
c语言·c++·后端
小年糕是糕手3 小时前
【C++】类和对象(二) -- 构造函数、析构函数
java·c语言·开发语言·数据结构·c++·算法·leetcode