前言
本文介绍一个基于 Qt/C++ 的任务池与 Worker 框架:应用场景、使用方式、架构设计思路、关键代码,以及构建与测试方法。
为什么需要 TaskPool + Worker
- 统一并发入口:将不同类型任务(CPU/IO/网络/图形/定时)统一提交到任务池,按优先级调度执行。
- 可观测与可控:内置任务状态、生命周期信号、进度与消息回传、取消与超时控制。
- 跨平台与高可用:基于 QtCore/QtNetwork/QtGui,适配 Windows/Linux/macOS,线程安全无泄漏。
构建与运行
- 本文代码使用 VS2019 + Qt 5.15.2 + CMake构建。
应用场景
- 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。
目录结构:

使用方式
- 定义一个
Worker子类,覆盖execute(CancellationToken&),在执行中检查取消/超时/暂停/停止。 - 创建
TaskPool,设置线程数与连接状态信号。 - 调用
submitTask提交任务函数(可绑定Worker),得到TaskHandle;需要时调用cancel()取消。 - 在主线程中通过信号槽获取生命周期与进度。
示例(合并自 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 子类与业务处理逻辑,并通过任务池实现稳定高效的并发控制。