Qt | 利用map创建多个线程和定时器

文章目录

  • [Qt | 利用map创建多个线程和定时器](#Qt | 利用map创建多个线程和定时器)
    • 一、核心思路与技术选型
      • [1. 核心需求](#1. 核心需求)
      • [2. 技术选型](#2. 技术选型)
      • [3. 关键注意事项](#3. 关键注意事项)
    • 二、完整实现代码
      • [1. 工作任务类(封装线程内的定时任务)](#1. 工作任务类(封装线程内的定时任务))
      • [2. 线程管理器(利用map管理多个线程和Worker)](#2. 线程管理器(利用map管理多个线程和Worker))
      • [3. 主函数/主窗口调用示例](#3. 主函数/主窗口调用示例)
    • 三、代码解析与核心亮点
      • [1. Map的核心作用](#1. Map的核心作用)
      • [2. 线程安全设计](#2. 线程安全设计)
      • [3. 扩展性优化](#3. 扩展性优化)
    • 四、常见问题与解决方案
    • 五、总结

Qt | 利用map创建多个线程和定时器

在Qt开发中,多线程与定时器的组合是处理异步任务、周期性任务的常见场景。比如批量处理文件、多设备实时监控、定时数据采集等需求,往往需要创建多个独立线程,并为每个线程绑定专属定时器。直接逐个创建线程和定时器会导致代码冗余、维护困难,而借助std::map(或QMap)管理这些对象,能实现对线程、定时器的集中化、结构化管控。本文基于Qt 5.12.12版本,详解如何利用map高效创建、管理多个线程和定时器,并结合实战案例演示完整实现。

一、核心思路与技术选型

1. 核心需求

  • 动态创建N个独立工作线程,每个线程执行专属的周期性任务;
  • 每个线程内绑定一个定时器,控制任务执行频率;
  • 统一管理所有线程和定时器,支持动态增删、状态监控。

2. 技术选型

组件 作用
std::map/QMap 以"标识ID-对象指针"的键值对形式,存储线程(QThread*)和定时器(QTimer*),便于按ID快速查找、管理
QThread 实现任务的异步执行,避免阻塞主线程
QTimer 在线程内实现周期性任务触发(注意:QTimer需与线程事件循环绑定)
QObject 封装工作任务,通过moveToThread将任务对象移至子线程执行

3. 关键注意事项

  • Qt的QTimer依赖事件循环,需确保线程启动后调用exec()开启事件循环;
  • 线程内的定时器必须在该线程的事件循环中创建/移动,否则无法正常触发;
  • 避免直接在主线程中操作子线程的对象,需通过信号槽实现线程间通信;
  • 销毁线程和定时器时需按"停止定时器→退出线程→等待线程结束→释放资源"的顺序,防止内存泄漏。

二、完整实现代码

1. 工作任务类(封装线程内的定时任务)

首先封装一个工作类,包含定时器和任务逻辑,该类的对象将被移至子线程执行:

cpp 复制代码
#include <QObject>
#include <QTimer>
#include <QThread>
#include <QDebug>
#include <map>

// 工作任务类:封装单个线程的定时任务
class Worker : public QObject
{
    Q_OBJECT
public:
    explicit Worker(int workerId, QObject *parent = nullptr)
        : QObject(parent), m_workerId(workerId)
    {
        // 创建定时器(初始未启动)
        m_timer = new QTimer(this);
        // 设置定时器单次触发(避免线程退出时定时器残留)
        m_timer->setSingleShot(false);
        // 绑定定时器触发信号到任务槽函数
        connect(m_timer, &QTimer::timeout, this, &Worker::doWork);
    }

    ~Worker()
    {
        // 销毁前停止定时器
        if (m_timer->isActive()) {
            m_timer->stop();
        }
        qDebug() << "Worker" << m_workerId << "destroyed";
    }

    // 启动定时器(指定间隔,单位:ms)
    void startTimer(int interval)
    {
        if (!m_timer->isActive()) {
            m_timer->start(interval);
            qDebug() << "Worker" << m_workerId << "timer started, interval:" << interval << "ms";
        }
    }

    // 停止定时器
    void stopTimer()
    {
        if (m_timer->isActive()) {
            m_timer->stop();
            qDebug() << "Worker" << m_workerId << "timer stopped";
        }
    }

    int workerId() const { return m_workerId; }

signals:
    // 任务完成信号(可选,用于主线程接收结果)
    void workFinished(int workerId, const QString& result);

private slots:
    // 定时执行的任务逻辑
    void doWork()
    {
        // 模拟业务逻辑:打印线程ID和任务ID
        QString result = QString("Worker %1 execute task in thread %2")
                             .arg(m_workerId)
                             .arg((quintptr)QThread::currentThreadId());
        qDebug() << result;
        // 发送任务完成信号
        emit workFinished(m_workerId, result);
    }

private:
    int m_workerId;    // 任务唯一标识
    QTimer* m_timer;   // 专属定时器
};

2. 线程管理器(利用map管理多个线程和Worker)

创建一个管理器类,通过std::map存储线程、Worker对象,提供创建、启动、停止、销毁的统一接口:

cpp 复制代码
// 线程定时器管理器:统一管理多个线程和定时器
class ThreadTimerManager : public QObject
{
    Q_OBJECT
public:
    explicit ThreadTimerManager(QObject *parent = nullptr)
        : QObject(parent)
    {
        // 确保管理器在主线程
        qDebug() << "Manager created in thread:" << (quintptr)QThread::currentThreadId();
    }

    ~ThreadTimerManager()
    {
        // 销毁所有线程和Worker
        destroyAllWorkers();
    }

    // 创建指定ID的工作线程和定时器
    bool createWorker(int workerId, int timerInterval)
    {
        // 检查ID是否已存在
        if (m_workerMap.contains(workerId) || m_threadMap.contains(workerId)) {
            qWarning() << "Worker" << workerId << "already exists";
            return false;
        }

        // 1. 创建线程
        QThread* thread = new QThread(this);
        // 2. 创建Worker对象
        Worker* worker = new Worker(workerId);
        // 3. 将Worker移至子线程
        worker->moveToThread(thread);

        // 4. 绑定线程信号槽(确保线程安全退出)
        // 线程启动后,启动Worker的定时器
        connect(thread, &QThread::started, [worker, timerInterval]() {
            worker->startTimer(timerInterval);
        });
        // 线程结束后,销毁Worker
        connect(thread, &QThread::finished, worker, &Worker::deleteLater);
        // 线程结束后,销毁自身
        connect(thread, &QThread::finished, thread, &QThread::deleteLater);

        // 5. 绑定Worker的任务完成信号(主线程接收)
        connect(worker, &Worker::workFinished, this, [](int id, const QString& res) {
            qDebug() << "Main thread receive: Worker" << id << "result:" << res;
        });

        // 6. 将线程和Worker存入map
        m_threadMap.insert(workerId, thread);
        m_workerMap.insert(workerId, worker);

        qDebug() << "Worker" << workerId << "created, thread ID:" << (quintptr)thread;
        return true;
    }

    // 启动指定ID的线程
    bool startWorker(int workerId)
    {
        if (!m_threadMap.contains(workerId)) {
            qWarning() << "Worker" << workerId << "not exists";
            return false;
        }

        QThread* thread = m_threadMap[workerId];
        if (thread->isRunning()) {
            qWarning() << "Worker" << workerId << "thread already running";
            return false;
        }

        thread->start(); // 启动线程(触发started信号,进而启动定时器)
        return true;
    }

    // 停止指定ID的线程
    bool stopWorker(int workerId)
    {
        if (!m_threadMap.contains(workerId) || !m_workerMap.contains(workerId)) {
            qWarning() << "Worker" << workerId << "not exists";
            return false;
        }

        QThread* thread = m_threadMap[workerId];
        Worker* worker = m_workerMap[workerId];

        // 1. 停止定时器
        worker->stopTimer();
        // 2. 退出线程事件循环
        thread->quit();
        // 3. 等待线程结束(避免资源未释放)
        thread->wait(1000); // 超时1秒

        qDebug() << "Worker" << workerId << "thread stopped";
        return true;
    }

    // 销毁指定ID的线程和Worker
    bool destroyWorker(int workerId)
    {
        // 先停止线程
        if (!stopWorker(workerId)) {
            return false;
        }

        // 从map中移除并释放(线程和Worker会通过finished信号自动销毁)
        m_threadMap.remove(workerId);
        m_workerMap.remove(workerId);

        qDebug() << "Worker" << workerId << "destroyed";
        return true;
    }

    // 销毁所有线程和Worker
    void destroyAllWorkers()
    {
        // 遍历所有Worker ID,逐个停止并销毁
        QList<int> workerIds = m_workerMap.keys();
        for (int id : workerIds) {
            destroyWorker(id);
        }
        m_threadMap.clear();
        m_workerMap.clear();
        qDebug() << "All workers destroyed";
    }

private:
    // 存储线程:key=WorkerID,value=QThread*
    std::map<int, QThread*> m_threadMap;
    // 存储Worker:key=WorkerID,value=Worker*
    std::map<int, Worker*> m_workerMap;
};

3. 主函数/主窗口调用示例

在主线程中使用管理器创建、启动多个线程和定时器:

cpp 复制代码
#include <QApplication>
#include <QWidget>
#include <QPushButton>
#include <QVBoxLayout>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // 创建主窗口
    QWidget window;
    window.setWindowTitle("Qt MultiThread + Timer (Map Manage)");
    window.resize(400, 300);

    // 创建管理器
    ThreadTimerManager manager;

    // 创建按钮:创建3个Worker
    QPushButton btnCreate("Create 3 Workers (ID:1/2/3)");
    // 创建按钮:启动所有Worker
    QPushButton btnStart("Start All Workers");
    // 创建按钮:停止所有Worker
    QPushButton btnStop("Stop All Workers");
    // 创建按钮:销毁所有Worker
    QPushButton btnDestroy("Destroy All Workers");

    // 布局
    QVBoxLayout* layout = new QVBoxLayout(&window);
    layout->addWidget(&btnCreate);
    layout->addWidget(&btnStart);
    layout->addWidget(&btnStop);
    layout->addWidget(&btnDestroy);

    // 绑定按钮信号
    // 1. 创建3个Worker,定时器间隔分别为1000ms、1500ms、2000ms
    QObject::connect(&btnCreate, &QPushButton::clicked, [&]() {
        manager.createWorker(1, 1000);
        manager.createWorker(2, 1500);
        manager.createWorker(3, 2000);
    });

    // 2. 启动3个Worker
    QObject::connect(&btnStart, &QPushButton::clicked, [&]() {
        manager.startWorker(1);
        manager.startWorker(2);
        manager.startWorker(3);
    });

    // 3. 停止3个Worker
    QObject::connect(&btnStop, &QPushButton::clicked, [&]() {
        manager.stopWorker(1);
        manager.stopWorker(2);
        manager.stopWorker(3);
    });

    // 4. 销毁3个Worker
    QObject::connect(&btnDestroy, &QPushButton::clicked, [&]() {
        manager.destroyAllWorkers();
    });

    window.show();
    return a.exec();
}

// 需在.pro文件中添加:
// QT += core gui widgets
// CONFIG += c++11
// SOURCES += main.cpp

三、代码解析与核心亮点

1. Map的核心作用

  • 结构化管理 :通过workerId作为key,将线程和Worker一一映射,可快速根据ID查找、操作指定对象;
  • 动态扩展:支持运行时动态创建/销毁任意ID的线程,无需修改底层逻辑;
  • 资源可控:遍历map即可批量管理所有线程,避免遗漏未销毁的线程/定时器。

2. 线程安全设计

  • Worker对象通过moveToThread移至子线程,定时器随Worker在子线程事件循环中运行,避免主线程阻塞;
  • 线程退出时,先停止定时器,再调用quit()退出事件循环,最后wait()等待线程结束,防止资源泄漏;
  • 线程和Worker的销毁通过finished信号绑定deleteLater,确保在事件循环中安全释放。

3. 扩展性优化

  • 可通过模板封装map,支持不同类型的任务对象;
  • 可增加getWorkerStatus(int id)接口,监控线程运行状态;
  • 可扩展定时器间隔动态调整接口(通过信号槽向子线程发送调整指令)。

四、常见问题与解决方案

问题现象 原因分析 解决方案
定时器不触发 线程未启动事件循环(未调用exec() 确保线程start()后自动启动事件循环(QThread默认启动);避免手动调用exit()
线程退出后崩溃 定时器未停止,仍向已销毁的Worker发送信号 销毁线程前先调用stopTimer()停止定时器
map访问崩溃 多线程同时操作map(如主线程增删、子线程读取) QMutex保护map的读写操作
内存泄漏 未等待线程finished就释放对象 调用thread->wait()等待线程结束后再移除map中的指针

五、总结

利用mapstd::map/QMap)管理Qt的多线程和定时器,可将分散的对象统一收纳,大幅提升代码的可维护性和扩展性。核心要点是:

  1. 封装独立的工作类,将定时器与任务逻辑绑定;
  2. 以唯一ID为键,通过map关联线程和工作对象;
  3. 严格遵循Qt线程安全规则,通过信号槽实现线程间通信;
  4. 销毁资源时按"停止定时器→退出线程→等待结束→释放资源"的顺序执行。

该方案适用于批量周期性任务、多设备并行监控、分布式数据采集等场景,是Qt多线程开发中结构化管理资源的典型实践。

相关推荐
用户47949283569152 小时前
性能提升 4000%!我是如何解决 运营看板 不能跨库&跨库查询慢这个难题的
数据库·后端·postgresql
电商API&Tina2 小时前
跨境电商 API 对接指南:亚马逊 + 速卖通接口调用全流程
大数据·服务器·数据库·python·算法·json·图搜索算法
robinson19883 小时前
验证崖山数据库标量子查询是否带有CACHE功能
数据库·oracle·cache·自定义函数·崖山·标量子查询
老华带你飞3 小时前
农产品销售管理|基于java + vue农产品销售管理系统(源码+数据库+文档)
java·开发语言·前端·数据库·vue.js·spring boot·后端
默默前行的虫虫3 小时前
nicegui地图总结
网络·python
北京盟通科技官方账号3 小时前
工业通讯底层对齐:EtherNet/IP Class 1 连接中的 32-bit Header 与内存映射逻辑
服务器·网络·网络协议·自动化·制造
SelectDB3 小时前
5 倍性能提升,Apache Doris TopN 全局优化详解|Deep Dive
数据库·apache
@淡 定3 小时前
消息队列使用场景
网络
Tom4i3 小时前
【网络优化】Android 如何监听系统网络连接成功
android·网络