当前位置: 首页 > news >正文

【Qt】线程池与全局信号实现异步协作

Qt线程池与全局信号实现异步协作

利用Qt的线程池并发执行任务,通过全局信号跨线程传递结果,配合事件循环阻塞等待,实现了多任务并行处理与主线程同步收尾

一、使用流程
  1. 初始化基础组件
    • 创建QCoreApplication实例(Qt 程序入口,提供事件循环核心)。
    • 定义GlobalSignal单例(全局信号载体,跨线程传递结果)。
  2. 准备结果存储与同步机制
    • QVariant result1/2/3:通用类型容器,存储不同任务的结果(支持任意数据类型,如字符串、自定义对象)。
    • QMutex mutex:保护共享结果变量,避免多线程同时写入导致的数据混乱(线程安全核心)。
    • finishedCount计数器:记录已完成的任务数,用于判断是否所有任务都执行完毕。
  3. 创建事件循环等待机制
    • QEventLoop loop:局部事件循环,让主线程在任务执行期间阻塞等待(而非直接退出),直到所有任务完成后通过loop.quit()退出。
  4. 连接全局信号与结果收集逻辑
    • 主线程连接GlobalSignal::resultReady信号,通过taskType(0/1/2)区分不同任务的结果,存入对应result变量。
    • 每次接收结果后,finishedCount递增,当等于总任务数(3)时,触发loop.quit(),结束等待。
  5. 提交任务到线程池
    • 通过QThreadPool::globalInstance()获取全局线程池,设置最大线程数(3)控制并发量。
    • 用wrapRunnable将 lambda 函数包装为QRunnable任务(自动销毁autoDelete(true)),通过pool->start()提交:
      • 任务内部模拟耗时操作(QThread::msleep),生成结果。
      • 通过QMetaObject::invokeMethod在主线程发射resultReady信号(Qt::QueuedConnection确保跨线程安全),传递结果和任务类型。
  6. 等待任务完成并处理结果
    • loop.exec()启动事件循环,主线程阻塞等待所有任务完成。
    • pool->waitForDone()确保线程池内所有任务彻底结束(避免残留线程)。
    • 打印所有结果,完成流程。
二、原理与知识点凝练
  1. Qt 线程池(QThreadPool
    • 作用:管理线程生命周期,避免频繁创建 / 销毁线程的开销,自动调度线程执行QRunnable任务。
    • 关键方法:start(QRunnable*)提交任务、setMaxThreadCount(int)控制最大并发数、waitForDone()阻塞等待所有任务完成。
  2. 任务封装(QRunnable与 lambda)
    • QRunnable是线程池任务的基类,通过重写run()定义任务逻辑。
    • wrapRunnable将 lambda 函数包装为QRunnable子类,简化任务定义(无需手动创建子类)。
    • setAutoDelete(true):任务执行后自动销毁,避免内存泄漏(核心机制)。
  3. 跨线程通信(全局信号 +QMetaObject::invokeMethod
    • 全局信号单例GlobalSignal:统一信号源,避免每个任务单独定义信号,简化多任务结果传递。
    • QMetaObject::invokeMethod:确保信号在主线程发射(Qt::QueuedConnection),解决跨线程信号槽的线程安全问题(Qt 信号槽跨线程默认通过事件队列传递,自动处理线程切换)。
  4. 线程安全(QMutex
    • 多线程同时写入共享结果变量(result1/2/3)时,QMutex通过互斥锁保证同一时间只有一个线程修改数据,避免竞态条件。
  5. 阻塞等待(QEventLoop
    • 主线程需要等待异步任务完成后再处理结果,QEventLoop提供局部事件循环,通过exec()阻塞、quit()唤醒,实现 “等待但不冻结应用” 的效果(适合需要同步等待异步结果的场景)。
  6. 通用结果存储(QVariant
    • 可存储任意 Qt 支持的类型(字符串、数字、自定义对象等),无需为不同任务结果定义特定变量类型,提升代码通用性。

通过 “线程池调度任务 + 全局信号传递结果 + 事件循环等待 + 互斥锁保安全” 的组合,实现了多任务并行执行与结果收集的核心需求。核心亮点是利用 Qt 的信号槽机制和事件循环,优雅解决了跨线程通信和同步等待问题,同时通过 lambda 和QVariant简化了代码实现。

#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();
}
http://www.dtcms.com/a/338706.html

相关文章:

  • 【qml-5】qml与c++交互(类型单例)
  • JVM垃圾收集器
  • Linux重置 root 密码:从原理到实操
  • 免费OCR工具支持哪些文档格式转换
  • 8.19打卡 DAY 46 通道注意力(SE注意力)
  • RPC高频问题与底层原理剖析
  • 在VSCode中进行Vue前端开发推荐的插件
  • 基于C语言基础对C++的进一步学习_知识补充、组合类、类中的静态成员与静态函数、类中的常对象和常成员函数、类中的this指针、类中的友元
  • Laya的适配模式选择
  • 使用 Ansys Discovery 探索外部空气动力学
  • 龙虎榜——20250819
  • python学习打卡day38
  • 上网行为管理-内容审计
  • 初识CNN05——经典网络认识2
  • GPT-5 上线风波深度复盘:从口碑两极到策略调整,OpenAI 的变与不变
  • 006.Redis 哨兵(Sentinel)架构实战
  • 多序列时间序列预测案例:scalecast库的使用
  • Back键的响应范围比Recent键大100%
  • 基于STM32+NBIOT设计的宿舍安防控制系统_264
  • python的社区互助养老系统
  • LLM 中 token 简介与 bert 实操解读
  • Vue中父子组件间的数据传递
  • oc-mirror plugin v2 错误could not establish the destination for the release i
  • 什么是STLC(软件测试生命周期)?
  • 招标网站用户规模评测:基于第三方流量数据的 10 大平台对比分析​
  • [Git] 如何拉取 GitHub 仓库的特定子目录
  • 05高级语言逻辑结构到汇编语言之逻辑结构转换 while (...) {...} 结构
  • GaussDB 并发自治事务数达到最大值处理案例
  • consul-基础概念
  • Leetcode 343. 整数拆分 动态规划