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

开源 C++ QT QML 开发(十三)多线程

           文章的目的为了记录使用QT QML开发学习的经历。开发流程和要点有些记忆模糊,赶紧记录,防止忘记。

 相关链接:

开源 C++ QT QML 开发(一)基本介绍

开源 C++ QT QML 开发(二)工程结构

开源 C++ QT QML 开发(三)常用控件

开源 C++ QT QML 开发(四)复杂控件--Listview

开源 C++ QT QML 开发(五)复杂控件--Gridview

开源 C++ QT QML 开发(六)自定义控件--波形图

开源 C++ QT QML 开发(七)自定义控件--仪表盘

推荐链接:

开源 C# 快速开发(一)基础知识

开源 C# 快速开发(二)基础控件

开源 C# 快速开发(三)复杂控件

开源 C# 快速开发(四)自定义控件--波形图

开源 C# 快速开发(五)自定义控件--仪表盘

开源 C# 快速开发(六)自定义控件--圆环

开源 C# 快速开发(七)通讯--串口

开源 C# 快速开发(八)通讯--Tcp服务器端

开源 C# 快速开发(九)通讯--Tcp客户端

开源 C# 快速开发(十)通讯--http客户端

开源 C# 快速开发(十一)线程

开源 C# 快速开发(十二)进程监控

开源 C# 快速开发(十三)进程--管道通讯

开源 C# 快速开发(十四)进程--内存映射

开源 C# 快速开发(十五)进程--windows消息

开源 C# 快速开发(十六)数据库--sqlserver增删改查

本章节主要内容是:Qt 多线程编程的例子,实现了线程之间的通讯,采用信号槽通讯。

1.代码分析

2.所有源码

3.效果演示

一、代码分析

 整体架构

主线程(main) ↔ ThreadController ↔ Worker(后台线程)
     ↓              ↓
   QML界面      信号槽通信


 Worker 类详细分析
构造函数 Worker::Worker()
 

Worker::Worker(QObject *parent) : QObject(parent), m_count(0)  // 初始化计数器为0
{m_timer = new QTimer(this);  // 创建定时器,父对象为thisconnect(m_timer, &QTimer::timeout, this, &Worker::doWork);
}


功能:

初始化数据成员 m_count

创建 QTimer 用于模拟周期性工作

连接定时器超时信号到工作槽函数

关键点:

使用 this 作为父对象,但后续会被 moveToThread(),父子关系会调整

定时器在工作者线程中运行

析构函数 Worker::~Worker()
 

Worker::~Worker()
{qDebug() << "Worker: 析构函数被调用";if (m_timer && m_timer->isActive()) {m_timer->stop();  // 停止定时器}
}


功能:确保在对象销毁时停止定时器

开始工作 Worker::startWork()
 

void Worker::startWork()
{qDebug() << "Worker: 开始工作,线程ID:" << QThread::currentThreadId();m_count = 0;           // 重置计数器m_timer->start(1000);  // 启动1秒间隔的定时器
}


调用时机:当收到来自主线程的 startWork 信号时
作用:启动后台工作流程

停止工作 Worker::stopWork()
 

void Worker::stopWork()
{if (m_timer->isActive()) {m_timer->stop();  // 停止定时器}qDebug() << "Worker: 停止工作";
}


功能:立即停止后台工作

核心工作函数 Worker::doWork()
 

void Worker::doWork()
{m_count++;QString data = QString("后台数据 %1 - 线程ID: %2").arg(m_count).arg((quintptr)QThread::currentThreadId());// 发送数据到主线程emit dataUpdated(data);qDebug() << "Worker: 发送数据:" << data;if(m_count >= 10) {  // 限制执行10次stopWork();emit workFinished();  // 通知工作完成}
}


执行流程:

增加计数器

生成包含线程ID的数据字符串

发射 dataUpdated 信号将数据发送到主线程

检查是否达到执行上限,如果是则停止并通知完成

 ThreadController 类详细分析
构造函数 ThreadController::ThreadController()
 

ThreadController::ThreadController(QObject *parent) : QObject(parent), m_workerThread(nullptr), m_worker(nullptr), m_isWorking(false)
{// 1. 创建线程和工作对象m_workerThread = new QThread(this);  // 父对象为thism_worker = new Worker();             // 无父对象,准备移动到线程// 2. 关键:将工作者移动到工作线程m_worker->moveToThread(m_workerThread);// 3. 建立信号槽连接connect(m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater);connect(this, &ThreadController::startWork, m_worker, &Worker::startWork);connect(this, &ThreadController::stopWork, m_worker, &Worker::stopWork);connect(m_worker, &Worker::dataUpdated, this, &ThreadController::onDataUpdated);connect(m_worker, &Worker::workFinished, this, &ThreadController::onWorkFinished);// 4. 启动线程(此时线程就绪,等待信号)m_workerThread->start();qDebug() << "ThreadController: 主线程ID:" << QThread::currentThreadId();
}


关键连接分析:

信号源    信号    接收者    槽函数    连接类型
m_workerThread    finished    m_worker    deleteLater    自动
ThreadController    startWork    Worker    startWork    队列
ThreadController    stopWork    Worker    stopWork    队列
Worker    dataUpdated    ThreadController    onDataUpdated    队列
Worker    workFinished    ThreadController    onWorkFinished    队列
重要细节:

moveToThread(m_workerThread) 是核心,它将 Worker 的事件循环转移到新线程

所有跨线程连接自动使用 Qt::QueuedConnection

线程启动后处于等待状态,直到收到开始信号

析构函数 ThreadController::~ThreadController()
 

ThreadController::~ThreadController()
{qDebug() << "ThreadController: 析构函数被调用";if (m_workerThread && m_workerThread->isRunning()) {// 1. 停止工作if (m_worker) {m_worker->stopWork();}// 2. 优雅退出m_workerThread->quit();m_workerThread->wait(1000);  // 等待1秒// 3. 强制终止(如果优雅退出失败)if (m_workerThread->isRunning()) {m_workerThread->terminate();m_workerThread->wait();}}
}


资源清理策略:

先尝试停止工作

请求线程退出并等待

超时后强制终止

由于 m_workerThread 的父对象是 this,会自动删除


QML接口函数 ThreadController::start() 和 stop()
 

void ThreadController::start()
{if (!m_isWorking) {setWorking(true);      // 更新状态emit startWork();      // 触发后台线程开始工作}
}void ThreadController::stop()
{if (m_isWorking) {emit stopWork();       // 触发后台线程停止工作setWorking(false);     // 更新状态}
}


设计模式:

作为 QML 调用的接口

先更新状态再发射信号,确保状态一致性

数据转发函数 ThreadController::onDataUpdated()
 

void ThreadController::onDataUpdated(const QString &data)
{// 在主线程中接收数据并转发给QMLqDebug() << "ThreadController: 接收数据 -" << data;emit newData(data);  // 转发到QML
}


线程上下文:

在主线程中执行(因为 ThreadController 在主线程)

将从 Worker 线程接收的数据转发给 QML

工作完成处理 ThreadController::onWorkFinished()
 

void ThreadController::onWorkFinished()
{qDebug() << "ThreadController: 工作完成";setWorking(false);  // 更新工作状态
}


作用:处理 Worker 的自然结束(执行完10次后)

状态管理函数 ThreadController::setWorking()
 

void ThreadController::setWorking(bool working)
{if (m_isWorking != working) {m_isWorking = working;emit isWorkingChanged();  // 通知QML属性变化qDebug() << "ThreadController: 工作状态改变为:" << working;}
}


QML集成关键:

通过 emit isWorkingChanged() 通知 QML 属性已更新

QML 的 isWorking 绑定会自动更新
main.cpp 分析
 

int main(int argc, char *argv[])
{QGuiApplication app(argc, argv);// 1. 创建控制器(在主线程)ThreadController controller;QQmlApplicationEngine engine;// 2. 注册为QML上下文属性engine.rootContext()->setContextProperty("threadController", &controller);// 3. 加载QMLengine.load(url);return app.exec();
}


重要点:

ThreadController controller 在主线程创建

通过 setContextProperty 暴露给 QML,名称为 threadController

完整执行流程


启动流程
QML点击开始 → threadController.start()

ThreadController → setWorking(true) + emit startWork()

Worker线程 → startWork() 启动定时器

每秒触发 → doWork() 生成数据 → emit dataUpdated()

数据流

Worker线程 → dataUpdated信号 → ThreadController主线程 → newData信号 → QML界面
停止流程
手动停止:


QML停止按钮 → threadController.stop() → emit stopWork() → Worker::stopWork()
自动停止:


Worker::doWork() (第10次) → stopWork() → emit workFinished() → ThreadController::onWorkFinished()

二、所有源码

worker.h文件源码

#ifndef WORKER_H
#define WORKER_H#include <QObject>
#include <QThread>
#include <QDebug>
#include <QTimer>class Worker : public QObject
{Q_OBJECTpublic:explicit Worker(QObject *parent = nullptr);~Worker();public slots:void startWork();void stopWork();private slots:void doWork();signals:void dataUpdated(const QString &data);void workFinished();private:QTimer *m_timer;int m_count;
};#endif // WORKER_H

worker.cpp文件源码

#include "worker.h"Worker::Worker(QObject *parent): QObject(parent), m_count(0)
{m_timer = new QTimer(this);connect(m_timer, &QTimer::timeout, this, &Worker::doWork);
}Worker::~Worker()
{qDebug() << "Worker: 析构函数被调用";if (m_timer && m_timer->isActive()) {m_timer->stop();}
}void Worker::startWork()
{qDebug() << "Worker: 开始工作,线程ID:" << QThread::currentThreadId();m_count = 0;m_timer->start(1000); // 每秒触发一次
}void Worker::stopWork()
{if (m_timer->isActive()) {m_timer->stop();}qDebug() << "Worker: 停止工作";
}void Worker::doWork()
{m_count++;QString data = QString("后台数据 %1 - 线程ID: %2").arg(m_count).arg((quintptr)QThread::currentThreadId());// 发送数据到主线程emit dataUpdated(data);qDebug() << "Worker: 发送数据:" << data;if(m_count >= 10) {stopWork();emit workFinished();}
}

threadController.h文件源码
#ifndef THREADCONTROLLER_H
#define THREADCONTROLLER_H#include <QObject>
#include <QThread>
#include "worker.h"class ThreadController : public QObject
{Q_OBJECT// 将状态属性暴露给QMLQ_PROPERTY(bool isWorking READ isWorking NOTIFY isWorkingChanged)public:explicit ThreadController(QObject *parent = nullptr);~ThreadController();// QML可调用的函数Q_INVOKABLE void start();Q_INVOKABLE void stop();bool isWorking() const;signals:void startWork();void stopWork();void newData(const QString &data);void isWorkingChanged();private slots:void onDataUpdated(const QString &data);void onWorkFinished();private:void setWorking(bool working);QThread *m_workerThread;Worker *m_worker;bool m_isWorking;
};#endif // THREADCONTROLLER_H

threadController.cpp文件源码

#include "threadcontroller.h"
#include <QDebug>ThreadController::ThreadController(QObject *parent): QObject(parent), m_workerThread(nullptr), m_worker(nullptr), m_isWorking(false)
{// 创建工作线程和工作者m_workerThread = new QThread(this);m_worker = new Worker();// 将工作者移动到工作线程m_worker->moveToThread(m_workerThread);// 连接信号槽 - 使用QueuedConnection确保跨线程安全connect(m_workerThread, &QThread::finished, m_worker, &QObject::deleteLater);connect(this, &ThreadController::startWork, m_worker, &Worker::startWork);connect(this, &ThreadController::stopWork, m_worker, &Worker::stopWork);connect(m_worker, &Worker::dataUpdated, this, &ThreadController::onDataUpdated);connect(m_worker, &Worker::workFinished, this, &ThreadController::onWorkFinished);// 启动线程(但不立即开始工作)m_workerThread->start();qDebug() << "ThreadController: 主线程ID:" << QThread::currentThreadId();
}ThreadController::~ThreadController()
{qDebug() << "ThreadController: 析构函数被调用";if (m_workerThread && m_workerThread->isRunning()) {// 停止工作if (m_worker) {m_worker->stopWork();}// 退出线程m_workerThread->quit();m_workerThread->wait(1000); // 等待1秒// 如果线程仍然在运行,强制终止if (m_workerThread->isRunning()) {m_workerThread->terminate();m_workerThread->wait();}}
}void ThreadController::start()
{if (!m_isWorking) {setWorking(true);emit startWork();}
}void ThreadController::stop()
{if (m_isWorking) {emit stopWork();setWorking(false);}
}bool ThreadController::isWorking() const
{return m_isWorking;
}void ThreadController::onDataUpdated(const QString &data)
{// 在主线程中接收数据并转发给QMLqDebug() << "ThreadController: 接收数据 -" << data;emit newData(data);
}void ThreadController::onWorkFinished()
{qDebug() << "ThreadController: 工作完成";setWorking(false);
}void ThreadController::setWorking(bool working)
{if (m_isWorking != working) {m_isWorking = working;emit isWorkingChanged();qDebug() << "ThreadController: 工作状态改变为:" << working;}
}

main.qml文件源码

import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.12
import QtQuick.Layouts 1.12
import QtQuick.Dialogs 1.2ApplicationWindow {id: windowwidth: 500height: 400visible: truetitle: qsTr("Qt QML 多线程示例 - 基于C++实现")minimumWidth: 400minimumHeight: 300ColumnLayout {anchors.fill: parentanchors.margins: 20spacing: 25// 标题Text {Layout.alignment: Qt.AlignHCentertext: "QML + C++ 多线程演示"font.bold: truefont.pixelSize: 18color: "#2c3e50"}// 状态显示区域Rectangle {Layout.fillWidth: trueLayout.preferredHeight: 80color: threadController.isWorking ? "#d4edda" : "#f8d7da"border.color: threadController.isWorking ? "#c3e6cb" : "#f5c6cb"border.width: 2radius: 8RowLayout {anchors.fill: parentanchors.margins: 10// 状态指示器Rectangle {Layout.preferredWidth: 20Layout.preferredHeight: 20radius: 10color: threadController.isWorking ? "#28a745" : "#dc3545"border.color: "#ffffff"border.width: 2}ColumnLayout {Layout.fillWidth: truespacing: 5Text {text: threadController.isWorking ? "后台线程运行中" : "后台线程已停止"font.bold: truefont.pixelSize: 16color: threadController.isWorking ? "#155724" : "#721c24"}Text {text: threadController.isWorking ?"正在从后台线程接收数据..." :"点击开始按钮启动后台线程"font.pixelSize: 12color: threadController.isWorking ? "#155724" : "#721c24"}}}}// 数据显示区域GroupBox {Layout.fillWidth: trueLayout.fillHeight: truetitle: "后台线程数据接收区"ScrollView {anchors.fill: parentTextArea {id: dataDisplayreadOnly: trueplaceholderText: "等待接收后台线程数据...\n数据将在这里显示"background: Rectangle {color: "#f8f9fa"border.color: "#dee2e6"radius: 4}font.family: "Courier New"font.pixelSize: 12selectByMouse: true}}}// 控制按钮区域RowLayout {Layout.alignment: Qt.AlignHCenterspacing: 20Button {text: "开始任务"enabled: !threadController.isWorkingonClicked: {dataDisplay.text = "开始接收后台线程数据...\n" +"================================\n";threadController.start();}background: Rectangle {color: parent.enabled ? "#28a745" : "#6c757d"radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}}Button {text: "停止任务"enabled: threadController.isWorkingonClicked: {threadController.stop();dataDisplay.text += "================================\n" +"任务被手动停止\n";}background: Rectangle {color: parent.enabled ? "#dc3545" : "#6c757d"radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}}Button {text: "清空显示"onClicked: {dataDisplay.text = "";}background: Rectangle {color: "#17a2b8"radius: 5}contentItem: Text {text: parent.textcolor: "white"horizontalAlignment: Text.AlignHCenterverticalAlignment: Text.AlignVCenterfont.bold: true}}}// 信息提示Text {Layout.alignment: Qt.AlignHCentertext: "提示: 后台线程每秒发送一次数据,发送10次后自动停止"font.pixelSize: 11color: "#6c757d"font.italic: true}}// 连接来自C++的信号Connections {target: threadControlleronNewData: {dataDisplay.text += data + "\n";// 自动滚动到底部dataDisplay.cursorPosition = dataDisplay.length;}}// 关于对话框MessageDialog {id: aboutDialogtitle: "关于"text: "多线程示例\n\n" +"演示如何使用C++实现后台线程,\n" +"并通过信号槽与QML主线程通信。\n\n" +"版本 1.0"icon: StandardIcon.Information}// 菜单栏menuBar: MenuBar {Menu {title: "文件"MenuItem {text: "退出"onTriggered: Qt.quit()}}Menu {title: "帮助"MenuItem {text: "关于"onTriggered: aboutDialog.open()}}}
}

main.cpp文件源码

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include <QDebug>
#include "threadcontroller.h"int main(int argc, char *argv[])
{QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);QGuiApplication app(argc, argv);// 设置应用程序信息app.setApplicationName("QML多线程示例");app.setApplicationVersion("1.0");app.setOrganizationName("Example Company");// 创建线程控制器实例ThreadController controller;QQmlApplicationEngine engine;// 将控制器实例注册为QML上下文属性engine.rootContext()->setContextProperty("threadController", &controller);// 加载QML文件const QUrl url(QStringLiteral("qrc:/main.qml"));QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,&app, [url](QObject *obj, const QUrl &objUrl) {if (!obj && url == objUrl) {qCritical() << "QML加载失败:" << url;QCoreApplication::exit(-1);} else {qDebug() << "QML界面加载成功";}}, Qt::QueuedConnection);engine.load(url);qDebug() << "应用程序启动完成";return app.exec();
}

三、效果演示

开始任务后,线程会发送数据,输出将会打印消息。

http://www.dtcms.com/a/460761.html

相关文章:

  • 企业如何建设网站,企业搭建网站的流程
  • HarmonyOS SaveButton深度解析:安全便捷的媒体资源保存方案
  • 如何用开源外卖系统源码打造私域O2O生态?技术+运营双轮驱动
  • {title:敏捷开发实战如何利用Scrum框架在30天内交付高质量软件}
  • 浏览器端音视频处理新选择:Mediabunny 让 Web 媒体开发飞起来
  • iOS 26 能耗监测全景,Adaptive Power、新电池视图
  • 微软警告:攻击者将Microsoft Teams武器化用于勒索软件、间谍活动及社会工程攻击
  • QT MVC中View的特点及使用注意事项
  • WAF防护的性能优化策略
  • MyBatis-Spring集成完全指南
  • 如何知道自己的台式电脑的所有硬件信息
  • 门户网站 商城系统免费的网络推广
  • Arbess从入门到实战(9) - 使用Arbess+GitLab实现PHP项目自动化部署
  • .Net Core 在Linux系统下创建服务
  • Vue ASP.Net Core WebApi 前后端传参
  • IntelliJ IDEA 编译内存设置全攻略:Shared heap size vs User-local heap size 区别详解(2025版)
  • 恩施网站建设公司asp源码下载网站
  • 《考研408数据结构》第四章(串和串的算法)复习笔记
  • Git 完全指南:从入门到精通掌握版本控制
  • Git初识
  • 如何解决 pip install -r requirements.txt 报错 Git 未安装,无法处理 VCS URL(git+https://…)问题
  • ArcGIS Pro 进程管理:自动化解决方案与最佳实践
  • ASP.NET Core Web API 发布到 IIS 服务器
  • PostgreSQL + Redis + Elasticsearch 实时同步方案实践:从触发器到高性能搜索
  • AWS Lambda 学习笔
  • Vue 与.Net Core WebApi交互时路由初探
  • 怎么建立自己公司的网站软文营销案例分析
  • 深圳专业网站建设公司辽宁工程建设招标网
  • 抖音a_bogus参数加密逆向
  • 【网络编程】网络通信基石:从局域网到跨网段通信原理探秘