Qt 多线程编程最佳实践

在现代软件开发中,多线程编程是提升应用性能和响应性的关键技术。Qt 作为一个强大的跨平台框架,提供了丰富的多线程支持,包括 QThread、QtConcurrent、信号槽机制等。本文将深入探讨 Qt 多线程编程的最佳实践,帮助开发者避免常见陷阱,构建高效、稳定的多线程应用。

一、线程创建与管理

1. 继承 QThread 方式
class WorkerThread : public QThread {Q_OBJECT
public:explicit WorkerThread(QObject *parent = nullptr) : QThread(parent) {}protected:void run() override {// 线程执行的代码for (int i = 0; i < 100; ++i) {// 执行耗时操作emit progressUpdated(i);// 检查线程是否被请求终止if (isInterruptionRequested()) {return;}// 线程休眠msleep(100);}emit finished();}signals:void progressUpdated(int value);void finished();
};// 使用示例
void startWorkerThread() {WorkerThread *thread = new WorkerThread();// 连接信号槽connect(thread, &WorkerThread::progressUpdated, this, &MyClass::updateProgress);connect(thread, &WorkerThread::finished, thread, &QObject::deleteLater);connect(thread, &WorkerThread::finished, this, &MyClass::threadFinished);// 启动线程thread->start();// 一段时间后终止线程// thread->requestInterruption();
}
2. 使用 QObject::moveToThread()
class Worker : public QObject {Q_OBJECT
public:explicit Worker(QObject *parent = nullptr) : QObject(parent) {}public slots:void doWork() {// 线程执行的代码for (int i = 0; i < 100; ++i) {// 执行耗时操作emit progressUpdated(i);// 处理事件队列QCoreApplication::processEvents();}emit finished();}signals:void progressUpdated(int value);void finished();
};// 使用示例
void startWorker() {QThread *thread = new QThread();Worker *worker = new Worker();// 将 worker 对象移动到新线程worker->moveToThread(thread);// 连接信号槽connect(thread, &QThread::started, worker, &Worker::doWork);connect(worker, &Worker::finished, thread, &QThread::quit);connect(worker, &Worker::finished, worker, &QObject::deleteLater);connect(thread, &QThread::finished, thread, &QObject::deleteLater);// 启动线程thread->start();
}
3. 使用 QtConcurrent
#include <QtConcurrent>// 耗时操作函数
void longRunningTask(int value) {// 模拟耗时操作QThread::sleep(2);qDebug() << "Task finished with value:" << value;
}// 使用 QtConcurrent::run
void runConcurrentTask() {// 在线程池中运行任务QtConcurrent::run(longRunningTask, 42);// 或者使用 lambda 表达式QtConcurrent::run([](int value) {// 执行耗时操作QThread::sleep(2);qDebug() << "Lambda task finished with value:" << value;}, 100);// 获取任务结果QFuture<void> future = QtConcurrent::run(longRunningTask, 99);// 可以检查任务状态if (future.isRunning()) {qDebug() << "Task is running";}// 等待任务完成future.waitForFinished();
}

二、线程间通信

1. 使用信号槽机制
class Producer : public QObject {Q_OBJECT
public:explicit Producer(QObject *parent = nullptr) : QObject(parent) {}public slots:void startProducing() {for (int i = 0; i < 10; ++i) {emit dataReady(i);QThread::msleep(500);}}signals:void dataReady(int value);
};class Consumer : public QObject {Q_OBJECT
public:explicit Consumer(QObject *parent = nullptr) : QObject(parent) {}public slots:void processData(int value) {qDebug() << "Received data:" << value;}
};// 使用示例
void setupProducerConsumer() {Producer producer;Consumer consumer;// 连接信号槽(自动连接方式)QObject::connect(&producer, &Producer::dataReady, &consumer, &Consumer::processData);// 启动生产producer.startProducing();
}
2. 使用队列进行线程间数据传递
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>class ThreadSafeQueue {
public:void enqueue(const QString &data) {QMutexLocker locker(&mutex);queue.enqueue(data);condition.wakeOne();}QString dequeue() {QMutexLocker locker(&mutex);// 如果队列为空,等待数据while (queue.isEmpty()) {condition.wait(&mutex);}return queue.dequeue();}private:QQueue<QString> queue;QMutex mutex;QWaitCondition condition;
};// 生产者线程
class ProducerThread : public QThread {Q_OBJECT
public:explicit ProducerThread(ThreadSafeQueue *queue, QObject *parent = nullptr): QThread(parent), m_queue(queue) {}protected:void run() override {for (int i = 0; i < 10; ++i) {m_queue->enqueue(QString("Data %1").arg(i));msleep(500);}}private:ThreadSafeQueue *m_queue;
};// 消费者线程
class ConsumerThread : public QThread {Q_OBJECT
public:explicit ConsumerThread(ThreadSafeQueue *queue, QObject *parent = nullptr): QThread(parent), m_queue(queue) {}protected:void run() override {for (int i = 0; i < 10; ++i) {QString data = m_queue->dequeue();qDebug() << "Consumed:" << data;}}private:ThreadSafeQueue *m_queue;
};

三、线程同步与互斥

1. 使用 QMutex
class Counter {
public:void increment() {QMutexLocker locker(&mutex);count++;}void decrement() {QMutexLocker locker(&mutex);count--;}int value() const {QMutexLocker locker(&mutex);return count;}private:mutable QMutex mutex;int count = 0;
};
2. 使用读写锁 QReadWriteLock
class DataCache {
public:QByteArray data() const {QReadLocker locker(&lock);return m_data;}void setData(const QByteArray &data) {QWriteLocker locker(&lock);m_data = data;}private:mutable QReadWriteLock lock;QByteArray m_data;
};
3. 使用信号量 QSemaphore
class ResourceManager {
public:ResourceManager(int maxResources) : semaphore(maxResources) {}void acquireResource() {semaphore.acquire();}void releaseResource() {semaphore.release();}private:QSemaphore semaphore;
};

四、线程池与任务管理

1. 使用 QThreadPool
#include <QRunnable>class Task : public QRunnable {
public:explicit Task(int id) : m_id(id) {// 设置任务自动删除setAutoDelete(true);}void run() override {qDebug() << "Task" << m_id << "started in thread" << QThread::currentThreadId();// 模拟耗时操作QThread::sleep(2);qDebug() << "Task" << m_id << "finished";}private:int m_id;
};// 使用示例
void useThreadPool() {QThreadPool *pool = QThreadPool::globalInstance();qDebug() << "Max threads:" << pool->maxThreadCount();// 创建并启动多个任务for (int i = 0; i < 10; ++i) {Task *task = new Task(i);pool->start(task);}// 等待所有任务完成pool->waitForDone();
}
2. 自定义线程池
class CustomThreadPool : public QObject {Q_OBJECT
public:explicit CustomThreadPool(int threadCount, QObject *parent = nullptr): QObject(parent) {// 创建工作线程for (int i = 0; i < threadCount; ++i) {QThread *thread = new QThread(this);thread->start();m_threads.append(thread);}// 创建任务队列m_taskQueue = new QQueue<RunnableTask*>();m_mutex = new QMutex();m_condition = new QWaitCondition();// 为每个线程创建工作者for (QThread *thread : m_threads) {Worker *worker = new Worker(m_taskQueue, m_mutex, m_condition);worker->moveToThread(thread);// 连接信号槽以处理工作完成connect(worker, &Worker::taskFinished, this, &CustomThreadPool::taskFinished);}}~CustomThreadPool() {// 停止所有线程{QMutexLocker locker(m_mutex);m_abort = true;m_condition->wakeAll();}foreach (QThread *thread, m_threads) {thread->quit();thread->wait();}delete m_condition;delete m_mutex;delete m_taskQueue;}void enqueueTask(RunnableTask *task) {QMutexLocker locker(m_mutex);m_taskQueue->enqueue(task);m_condition->wakeOne();}signals:void taskFinished(RunnableTask *task);private:QList<QThread*> m_threads;QQueue<RunnableTask*> *m_taskQueue;QMutex *m_mutex;QWaitCondition *m_condition;bool m_abort = false;
};// 工作者类
class Worker : public QObject {Q_OBJECT
public:explicit Worker(QQueue<RunnableTask*> *taskQueue, QMutex *mutex, QWaitCondition *condition, QObject *parent = nullptr): QObject(parent), m_taskQueue(taskQueue), m_mutex(mutex), m_condition(condition) {// 启动工作循环QMetaObject::invokeMethod(this, &Worker::work, Qt::QueuedConnection);}public slots:void work() {while (true) {QMutexLocker locker(m_mutex);// 等待任务while (m_taskQueue->isEmpty() && !m_abort) {m_condition->wait(m_mutex);}if (m_abort) {return;}// 获取任务RunnableTask *task = m_taskQueue->dequeue();locker.unlock();// 执行任务task->run();// 发出任务完成信号emit taskFinished(task);}}signals:void taskFinished(RunnableTask *task);private:QQueue<RunnableTask*> *m_taskQueue;QMutex *m_mutex;QWaitCondition *m_condition;bool m_abort = false;
};

五、GUI 线程与工作线程

1. 避免在 GUI 线程执行耗时操作
// 错误做法:在 GUI 线程执行耗时操作
void badExample() {// 模拟耗时操作QThread::sleep(5);// 更新 UI(UI 会在 5 秒内冻结)ui->label->setText("Operation completed");
}// 正确做法:使用工作线程
void goodExample() {QThread *thread = new QThread();// 创建工作者class Worker : public QObject {Q_OBJECTpublic slots:void doWork() {// 模拟耗时操作QThread::sleep(5);// 发送结果到主线程emit resultReady("Operation completed");}signals:void resultReady(const QString &result);};Worker *worker = new Worker();worker->moveToThread(thread);// 连接信号槽connect(thread, &QThread::started, worker, &Worker::doWork);connect(worker, &Worker::resultReady, this, [this](const QString &result) {// 在主线程更新 UIui->label->setText(result);});connect(worker, &Worker::resultReady, thread, &QThread::quit);connect(worker, &Worker::resultReady, worker, &QObject::deleteLater);connect(thread, &QThread::finished, thread, &QObject::deleteLater);// 启动线程thread->start();
}
2. 使用 QtConcurrent::run 和 QFutureWatcher
#include <QtConcurrent>
#include <QFutureWatcher>void updateUiWithResult(const QString &result) {ui->label->setText(result);
}void useFutureWatcher() {// 创建并启动任务QFuture<QString> future = QtConcurrent::run([]() {// 模拟耗时操作QThread::sleep(3);return "Task completed";});// 创建监听器QFutureWatcher<QString> *watcher = new QFutureWatcher<QString>(this);// 连接信号槽connect(watcher, &QFutureWatcher<QString>::finished, this, [this, watcher]() {// 获取结果并更新 UIQString result = watcher->result();updateUiWithResult(result);// 清理watcher->deleteLater();});// 设置监听器watcher->setFuture(future);
}

六、线程安全的设计模式

1. 单例模式的线程安全实现
class Singleton {
public:static Singleton* instance() {// 使用双重检查锁定模式if (!m_instance) {QMutexLocker locker(&m_mutex);if (!m_instance) {m_instance = new Singleton();}}return m_instance;}// 禁用拷贝构造函数和赋值运算符Singleton(const Singleton&) = delete;Singleton& operator=(const Singleton&) = delete;private:Singleton() {}~Singleton() {}static Singleton* m_instance;static QMutex m_mutex;
};// 静态成员初始化
Singleton* Singleton::m_instance = nullptr;
QMutex Singleton::m_mutex;
2. 生产者-消费者模式
class ProducerConsumer {
public:void produce(const QString &data) {QMutexLocker locker(&m_mutex);// 等待缓冲区有空间while (m_buffer.size() >= m_maxSize) {m_bufferNotFull.wait(&m_mutex);}// 添加数据到缓冲区m_buffer.enqueue(data);// 通知消费者有新数据m_bufferNotEmpty.wakeOne();}QString consume() {QMutexLocker locker(&m_mutex);// 等待缓冲区有数据while (m_buffer.isEmpty()) {m_bufferNotEmpty.wait(&m_mutex);}// 从缓冲区取出数据QString data = m_buffer.dequeue();// 通知生产者有空间m_bufferNotFull.wakeOne();return data;}private:QQueue<QString> m_buffer;int m_maxSize = 10;QMutex m_mutex;QWaitCondition m_bufferNotEmpty;QWaitCondition m_bufferNotFull;
};

七、调试与性能优化

1. 线程调试技巧
// 打印当前线程 ID
qDebug() << "Current thread ID:" << QThread::currentThreadId();// 使用 QThread::currentThread() 获取当前线程对象
QThread *currentThread = QThread::currentThread();// 在线程中设置名称以便调试
void MyThread::run() {// 设置线程名称QThread::currentThread()->setObjectName("MyWorkerThread");// 线程执行代码// ...
}
2. 性能优化建议
// 使用线程局部存储(Thread Local Storage)
static thread_local QHash<QString, QString> threadLocalData;// 在适当的地方使用 QReadWriteLock 代替 QMutex
class DataCache {
public:QByteArray data() const {QReadLocker locker(&m_lock);return m_data;}void setData(const QByteArray &data) {QWriteLocker locker(&m_lock);m_data = data;}private:mutable QReadWriteLock m_lock;QByteArray m_data;
};// 使用无锁数据结构(QtConcurrent::blockingMapped 等)
QList<int> inputList = {1, 2, 3, 4, 5};
QList<int> outputList = QtConcurrent::blockingMapped(inputList, [](int value) {return value * 2;
});

八、总结

Qt 提供了丰富的多线程编程工具和类库,合理使用这些工具可以显著提升应用性能和响应性。在进行 Qt 多线程编程时,应遵循以下最佳实践:

  1. 选择合适的线程创建方式:根据需求选择继承 QThread、使用 moveToThread() 或 QtConcurrent
  2. 优先使用信号槽进行线程间通信:信号槽机制是线程安全的,能简化线程间数据传递
  3. 正确使用同步原语:使用 QMutex、QReadWriteLock、QSemaphore 等避免竞态条件
  4. 避免在 GUI 线程执行耗时操作:保持 UI 响应性
  5. 合理使用线程池:避免创建过多线程导致系统资源耗尽
  6. 设计线程安全的类和接口:考虑多线程环境下的资源竞争问题
  7. 仔细调试和优化多线程代码:使用工具检测死锁、竞态条件等问题

通过遵循这些最佳实践,开发者可以充分发挥 Qt 多线程编程的优势,构建高效、稳定、响应迅速的跨平台应用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/news/916611.shtml
繁体地址,请注明出处:http://hk.pswp.cn/news/916611.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Photo Studio PRO 安卓版:专业级照片编辑的移动解决方案

Photo Studio PRO 安卓版是一款功能强大的专业级照片编辑应用&#xff0c;旨在为用户提供丰富而强大的编辑工具和特效&#xff0c;帮助用户轻松地对照片进行美化和修饰。无论是摄影爱好者还是专业摄影师&#xff0c;都能通过这款应用实现从基础调整到高级合成的全流程编辑。 核…

2025高考志愿怎么填?张雪峰最新“保底”推荐来了!这4个专业专科也能拿高薪,毕业不愁!

专业选得好&#xff0c;就业跑不了&#xff01;2025年高考落幕&#xff0c;现在是决战未来的关键时刻&#xff0c;选专业比选学校更重要&#xff01; 今天&#xff0c;学长就根据张雪峰老师多次力荐、再结合2024年就业大数据&#xff0c;给大家盘点4个紧缺人才专业&#xff0c…

C++初学者4——标准数据类型

先导&#xff1a; 目录 一、整形 二、浮点型 &#xff01;保留指定小数位数 三、布尔类型 关系运算 逻辑运算 ​C逻辑运算四句口诀​ 四、字符型 ASCll码 C中的字符表示 字符比较 ASCII中的常用转换 大小写转换 转换成0~25 五、数据类型隐式转换 ​1. 隐式转…

HCIP的MGRE综合实验1

拓扑图&#xff1a;二、实验要求 1、R5为ISP&#xff0c;只能进行IP地址配置&#xff0c;其所有地址均配为公有Ip地址;2、R1和R5间使用PPP的PAP认证&#xff0c;R5为主认证方&#xff1b;R2与R5之间使用PPP的CHAP认证&#xff0c;R5为主认证方;R3与R5之间使用HDLC封装;3、R2、R…

Go语言实战案例-链表的实现与遍历

在数据结构的世界中&#xff0c;链表&#xff08;Linked List&#xff09; 是一种经典的线性结构&#xff0c;它以灵活的插入与删除能力著称。链表不像数组那样需要连续的内存空间&#xff0c;而是通过节点指针连接形成一条“链”。本篇我们将使用 Go 语言实现一个单向链表&…

C++常见的仿函数,预定义函数,functor,二元操作函数(对vector操作,加减乘除取余位运算等 )

C 标准库在 <functional> 头文件中为我们提供了一套非常方便的预定义函数对象&#xff08;也称为“仿函数”或 “functor”&#xff09;&#xff0c;它们可以像变量一样直接传递给 std::reduce 和其他标准算法。 你提到的 std::bit_or 和 std::multiplies 就是其中的成员…

【RH134 问答题】第 6 章 管理 SELinux 安全性

目录SELinux 是如何保护资源的&#xff1f;什么是自由决定的访问控制(DAC)&#xff1f;它有什么特点&#xff1f;什么是强制访问控制(MAC)&#xff1f;它有什么特点&#xff1f;什么是 SELinux 上下文&#xff1f;setenforce 0 命令的作用是什么&#xff1f;定义一条 SELinux 文…

【MacOS】发展历程

很高兴为您详细介绍 macOS 的详细发展历程。macOS 是苹果公司开发的操作系统&#xff0c;用于 Mac 电脑、iPad 和 Apple TV 等设备。以下是 macos 的主要版本和发展历程&#xff1a;1. System 7 (1991)发布日期&#xff1a;1991年特点&#xff1a;引入多任务处理功能。改进了拖…

智慧社区项目开发(二)——基于 JWT 的登录验证功能实现详解

在 Web 应用中&#xff0c;登录验证是保障系统安全的核心环节。本文将结合具体接口文档&#xff0c;详细讲解如何基于 JWT&#xff08;JSON Web Token&#xff09;实现登录验证功能&#xff0c;包括 JWT 配置、工具类封装、登录流程处理等关键步骤&#xff0c;帮助开发者快速理…

Jmeter的元件使用介绍:(七)后置处理器详解

Jmeter的后置处理器主要用于取样器执行后的提取数据操作。 Jmeter常用的后置处理器有:Json提取器、正则表达式提取器、边界提取器、Beanshell后置处理器。此外还有Xpath提取器、CSS选择器提取器等&#xff0c;由于这两项多用前端页面提取元素&#xff0c;目前的项目基本都是采…

Allure的安装,在Pytest中的简单使用以及生成测试报告

目录 1.Allure的安装 1--下载网址 2--选择对应系统版本下载 3--配置Allure环境变量 4--验证安装是否成功 5--配置JAVAJDK的环境变量&#xff08;如果已经配置&#xff0c;可以忽视这一步&#xff09; 2.python中pytestAllure 1--python安装Allure包 2--生成测试报告 1--使用pyt…

Oracle 数据库报 ora-00257 错误并且执行alter system switch logfile 命令卡死的解决过程

Oracle 数据库报 ora-00257 错误并且执行alter system switch logfile 命令卡死的解决过程 7月26日下午&#xff0c;某医院用户的 HIS 系统无法连接&#xff0c;报如下错误&#xff1a;初步判断是归档日志问题。 用户的 HIS 系统数据库是双节点 Oracle 11g Rac 集群。登录服务器…

ArKTS:List 数组

一种&#xff1a;/**# encoding: utf-8# 版权所有 2025 ©涂聚文有限公司™ # 许可信息查看&#xff1a;言語成了邀功盡責的功臣&#xff0c;還需要行爲每日來值班嗎# 描述&#xff1a; 数组# Author : geovindu,Geovin Du 涂聚文.# IDE : DevEco Studio 5.1.1 …

Spring Boot 3整合Spring AI实战:9轮面试对话解析AI应用开发

Spring Boot 3整合Spring AI实战&#xff1a;9轮面试对话解析AI应用开发 第1轮&#xff1a;基础配置与模型调用 周先生&#xff1a;cc&#xff0c;先聊聊Spring AI的基础配置吧。如何在Spring Boot 3项目中集成Ollama&#xff1f; cc&#xff1a;我们可以通过OllamaConfig.java…

标准SQL语句示例

一、基础操作1. 数据库操作-- 1. 创建数据库 CREATE DATABASE 数据库名称 CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;-- 2. 删除数据库 DROP DATABASE IF EXISTS 数据库名称;-- 3. 选择数据库 USE 数据库名称;-- 4. 显示所有数据库 SHOW DATABASES;-- 5. 查看数据库创…

STM32-基本定时器

一.基本定时器简介 STM32F1 系列共有 8 个定时器&#xff0c;分别为&#xff1a;基本定时器、通用定时器、高级定时器。基本定时器 TIM6 和 TIM7 是一个 16 位的只能向上计数的定时器&#xff0c;只能定时&#xff0c;没有外部IO。 二.基本定时器功能 上图为基本定时器的功能框…

ofd文件转pdf

主要后端使用Java实现&#xff0c;前端可随意搭配http请求添加依赖&#xff1a;<!-- OFD解析与转换库 --><dependency><groupId>org.ofdrw</groupId><artifactId>ofdrw-converter</artifactId><version>1.17.9</version></…

4.应用层自定义协议与序列化

1.应用层程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层1.1再谈“协议”协议是一种 "约定". socket api 的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些 "结构化的数据" 怎么办呢…

【QT搭建opencv环境】

本文参考以下文章&#xff1a; https://blog.csdn.net/weixin_43763292/article/details/112975207 https://blog.csdn.net/qq_44743171/article/details/124335100 使用软件 QT 5.14.2下载地址&#xff1a;download.qt.io 选择版本&#xff1a;Qt 5.14.2 Qt 5.14.2百度网盘链接…

golang--函数栈

一、函数栈的组成结构&#xff08;栈帧&#xff09; 每个函数调用对应一个栈帧&#xff0c;包含以下核心部分&#xff1a; 1. 参数区 (Arguments) 位置&#xff1a;栈帧顶部&#xff08;高地址端&#xff09;内容&#xff1a; 函数调用时传入的参数按从右向左顺序压栈&#xff…