Qt线程池与全局信号实现异步协作
利用Qt的线程池并发执行任务,通过全局信号跨线程传递结果,配合事件循环阻塞等待,实现了多任务并行处理与主线程同步收尾
一、使用流程
- 初始化基础组件
- 创建
QCoreApplication
实例(Qt 程序入口,提供事件循环核心)。 - 定义
GlobalSignal
单例(全局信号载体,跨线程传递结果)。
- 创建
- 准备结果存储与同步机制
QVariant result1/2/3
:通用类型容器,存储不同任务的结果(支持任意数据类型,如字符串、自定义对象)。QMutex mutex
:保护共享结果变量,避免多线程同时写入导致的数据混乱(线程安全核心)。finishedCount
计数器:记录已完成的任务数,用于判断是否所有任务都执行完毕。
- 创建事件循环等待机制
QEventLoop loop
:局部事件循环,让主线程在任务执行期间阻塞等待(而非直接退出),直到所有任务完成后通过loop.quit()
退出。
- 连接全局信号与结果收集逻辑
- 主线程连接
GlobalSignal::resultReady
信号,通过taskType
(0/1/2)区分不同任务的结果,存入对应result
变量。 - 每次接收结果后,
finishedCount
递增,当等于总任务数(3)时,触发loop.quit()
,结束等待。
- 主线程连接
- 提交任务到线程池
- 通过
QThreadPool::globalInstance()
获取全局线程池,设置最大线程数(3)控制并发量。 - 用wrapRunnable将 lambda 函数包装为QRunnable任务(自动销毁autoDelete(true)),通过pool->start()提交:
- 任务内部模拟耗时操作(
QThread::msleep
),生成结果。 - 通过
QMetaObject::invokeMethod
在主线程发射resultReady
信号(Qt::QueuedConnection
确保跨线程安全),传递结果和任务类型。
- 任务内部模拟耗时操作(
- 通过
- 等待任务完成并处理结果
loop.exec()
启动事件循环,主线程阻塞等待所有任务完成。pool->waitForDone()
确保线程池内所有任务彻底结束(避免残留线程)。- 打印所有结果,完成流程。
二、原理与知识点凝练
- Qt 线程池(
QThreadPool
)- 作用:管理线程生命周期,避免频繁创建 / 销毁线程的开销,自动调度线程执行
QRunnable
任务。 - 关键方法:
start(QRunnable*)
提交任务、setMaxThreadCount(int)
控制最大并发数、waitForDone()
阻塞等待所有任务完成。
- 作用:管理线程生命周期,避免频繁创建 / 销毁线程的开销,自动调度线程执行
- 任务封装(
QRunnable
与 lambda)QRunnable
是线程池任务的基类,通过重写run()
定义任务逻辑。wrapRunnable
将 lambda 函数包装为QRunnable
子类,简化任务定义(无需手动创建子类)。setAutoDelete(true)
:任务执行后自动销毁,避免内存泄漏(核心机制)。
- 跨线程通信(全局信号 +
QMetaObject::invokeMethod
)- 全局信号单例
GlobalSignal
:统一信号源,避免每个任务单独定义信号,简化多任务结果传递。 QMetaObject::invokeMethod
:确保信号在主线程发射(Qt::QueuedConnection
),解决跨线程信号槽的线程安全问题(Qt 信号槽跨线程默认通过事件队列传递,自动处理线程切换)。
- 全局信号单例
- 线程安全(
QMutex
)- 多线程同时写入共享结果变量(
result1/2/3
)时,QMutex
通过互斥锁保证同一时间只有一个线程修改数据,避免竞态条件。
- 多线程同时写入共享结果变量(
- 阻塞等待(
QEventLoop
)- 主线程需要等待异步任务完成后再处理结果,
QEventLoop
提供局部事件循环,通过exec()
阻塞、quit()
唤醒,实现 "等待但不冻结应用" 的效果(适合需要同步等待异步结果的场景)。
- 主线程需要等待异步任务完成后再处理结果,
- 通用结果存储(
QVariant
)- 可存储任意 Qt 支持的类型(字符串、数字、自定义对象等),无需为不同任务结果定义特定变量类型,提升代码通用性。
通过 "线程池调度任务 + 全局信号传递结果 + 事件循环等待 + 互斥锁保安全" 的组合,实现了多任务并行执行与结果收集的核心需求。核心亮点是利用 Qt 的信号槽机制和事件循环,优雅解决了跨线程通信和同步等待问题,同时通过 lambda 和QVariant
简化了代码实现。
c++
#include <QCoreApplication>
#include <QThreadPool>
#include <QRunnable>
#include <QEventLoop>
#include <QMutex>
#include <QDebug>
#include <QVariant>
// 1. 全局信号载体(单例):用于跨线程传递结果
class GlobalSignal : public QObject {
Q_OBJECT
public:
static GlobalSignal* instance() {
static GlobalSignal ins;
return &ins;
}
signals:
// 信号:taskType区分任务类型,result传递结果(通用类型QVariant)
void resultReady(int taskType, const QVariant& result);
};
// 2. 任务包装器:将lambda转换为QRunnable
auto wrapRunnable = [](std::function<void()> func) {
class LambdaRunnable : public QRunnable {
public:
LambdaRunnable(std::function<void()> f) : func(f) {
setAutoDelete(true); // 任务执行后自动销毁
}
void run() override { func(); } // 执行任务逻辑
private:
std::function<void()> func;
};
return new LambdaRunnable(func);
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
// 3. 准备接收结果的变量(根据任务类型定义)
QVariant result1, result2, result3; // 用QVariant存储任意类型结果
QMutex mutex; // 保护结果变量的线程安全
int finishedCount = 0; // 任务完成计数器
const int totalTasks = 3; // 总任务数
// 4. 事件循环:等待所有任务完成
QEventLoop loop;
// 5. 连接全局信号,收集结果
QObject::connect(GlobalSignal::instance(), &GlobalSignal::resultReady,
[&](int taskType, const QVariant& result) {
QMutexLocker locker(&mutex); // 加锁保护共享变量
// 根据任务类型存储结果
switch (taskType) {
case 0: result1 = result; break;
case 1: result2 = result; break;
case 2: result3 = result; break;
}
// 所有任务完成后退出事件循环
if (++finishedCount == totalTasks) {
loop.quit();
}
});
// 6. 获取线程池实例
QThreadPool* pool = QThreadPool::globalInstance();
pool->setMaxThreadCount(3); // 设置最大线程数
// 7. 提交任务1:模拟任务(如查询1)
pool->start(wrapRunnable([=]() {
// 模拟耗时操作(如数据库查询)
QThread::msleep(1000);
QVariant result("任务1结果"); // 实际场景替换为真实结果
// 发送结果到主线程(通过全局信号)
QMetaObject::invokeMethod(GlobalSignal::instance(), [=]() {
emit GlobalSignal::instance()->resultReady(0, result);
}, Qt::QueuedConnection);
}));
// 8. 提交任务2:模拟任务(如查询2)
pool->start(wrapRunnable([=]() {
QThread::msleep(800);
QVariant result("任务2结果");
QMetaObject::invokeMethod(GlobalSignal::instance(), [=]() {
emit GlobalSignal::instance()->resultReady(1, result);
}, Qt::QueuedConnection);
}));
// 9. 提交任务3:模拟任务(如查询3)
pool->start(wrapRunnable([=]() {
QThread::msleep(1200);
QVariant result("任务3结果");
QMetaObject::invokeMethod(GlobalSignal::instance(), [=]() {
emit GlobalSignal::instance()->resultReady(2, result);
}, Qt::QueuedConnection);
}));
// 10. 等待所有任务完成
loop.exec(); // 阻塞等待事件循环退出
pool->waitForDone(); // 确保线程池所有任务结束
// 11. 处理结果
qDebug() << "最终结果:";
qDebug() << "任务1:" << result1.toString();
qDebug() << "任务2:" << result2.toString();
qDebug() << "任务3:" << result3.toString();
return a.exec();
}