【QT随笔】结合应用案例一文完美概括QT中的队列(Queue)
队列(Queue)是一种线性数据结构 ,其核心规则为先进先出(FIFO, First-In-First-Out) : 新元素在队尾 插入(enqueue),旧元素从队头移除(dequeue);先到达的元素先被服务,次序严格保序。
(关注不迷路哈!!!)
文章目录
- 【QT随笔】结合应用案例一文完美概括QT中的队列(Queue)
-
- 前言
- [一、使用 Qt 提供的容器类 QQueue 实现队列数据结构](#一、使用 Qt 提供的容器类 QQueue 实现队列数据结构)
-
- [1. 元素操作](#1. 元素操作)
- [2. 容量查询](#2. 容量查询)
- [3. 访问元素](#3. 访问元素)
- [4. 辅助功能](#4. 辅助功能)
- [二、使用事件队列 QEvent 与事件循环 QEventLoop 实现消息队列](#二、使用事件队列 QEvent 与事件循环 QEventLoop 实现消息队列)
-
- [1. 事件循环 (QEventLoop) 工作机制](#1. 事件循环 (QEventLoop) 工作机制)
- [2. QEvent 类层次结构](#2. QEvent 类层次结构)
- [3. 事件投递方式对比](#3. 事件投递方式对比)
- [4. 自定义事件创建与处理](#4. 自定义事件创建与处理)
- 三、多线程任务队列
- 四、线程安全队列实现
- 五、队列应用场景与实战案例
- 六、队列性能优化与最佳实践
- 七、不同类型队列对比汇总
- 总结
前言
队列(Queue)是计算机科学中一种基础且重要的数据结构 ,它遵循先进先出 (FIFO, First-In-First-Out)的原则。在Qt框架中,队列 不仅作为数据容器
存在,更是事件处理、多线程编程和任务调度
的核心机制。本文将全面探讨Qt中队列的实现方式、应用场景和最佳实践,帮助开发者更好地利用这一数据结构提升程序性能与可维护性。
一、使用 Qt 提供的容器类 QQueue 实现队列数据结构
📌 核心定位
QQueue 是Qt框架基于 QList 提供的模板容器类,专门用于实现队列数据结构。它提供了高效的入队(enqueue)和出队(dequeue)操作,适用于需要严格按顺序处理数据的场景。
🔑 核心功能
QQueue 提供了一系列直观易用的接口方法:
1. 元素操作
enqueue(const T &value)
:队尾添加元素dequeue()
:移除并返回队首元素(调用时确保队列不为空)head()
:返回队首元素(不移除)
2. 容量查询
isEmpty()
:检查队列是否为空size()
:返回队列元素数量
3. 访问元素
first()
:返回队首元素last()
:返回队尾元素at(int index)
:返回指定位置元素
4. 辅助功能
clear()
:清空队列contains(const T &value)
:检查是否包含某元素count(const T &value)
:统计某元素出现次数
cpp
#include <QDebug>
#include <QQueue>
int main()
{
QQueue<int> queue; // 创建 Int 类型的队列对象
// 入队操作
queue.enqueue(10); // 入队元素
queue.enqueue(20);
queue.enqueue(30);
// 查看队列状态
qDebug() << "Queue size:" << queue.size(); // 输出: 3 // 获取队列大小
qDebug() << "Queue head:" << queue.head(); // 输出: 10 // 查看队列的头部元素
qDebug() << "Is empty:" << queue.isEmpty(); // 输出: false // 检查队列是否为空
// 获取队列中的第一个和最后一个元素
qDebug() << "第一个元素:" << queue.first(); // 输出: 第一个元素: 10
qDebug() << "最后一个元素:" << queue.last(); // 输出: 最后一个元素: 30
// 检查队列是否包含特定值,contains 继承自 `QList` 的函数
qDebug() << "Queue contains 20:" << queue.contains(20); // 输出: Queue contains 20: true
// 出队操作
while (!queue.isEmpty())
{
int value = queue.dequeue(); // 出队元素
qDebug() << "出队元素:" << value;
}
// 出队之后,检查队列是否包含特定值(20)
qDebug() << "Queue contains 20:" << queue.contains(20); // 输出: Queue contains 20: false
queue.clear(); // 清空队列,移除队列中的所有元素
return 0;
}
cpp
Queue size: 3
Queue head: 10
Is empty: false
Queue contains 20: true
第一个元素: 10
最后一个元素: 30
出队元素: 10
出队元素: 20
出队元素: 30
Queue contains 20: false
QQueue的基本操作流程,包括创建队列、入队、出队以及循环处理直到队列为空的过程
二、使用事件队列 QEvent 与事件循环 QEventLoop 实现消息队列
📌 核心定位
Qt的事件队列
和事件循环
是框架运行的核心机制,这套机制不仅处理GUI应用程序的用户交互
,更是所有异步操作
和跨线程通信
的基石。通过 QEventLoop 对事件的分发和处理,Qt能够高效地处理用户输入、定时器事件和异步操作,确保应用程序既响应迅速又不会阻塞。
🔑 核心功能
1. 事件循环 (QEventLoop) 工作机制
事件循环 是Qt应用程序的心脏,通常由QApplication::exec()
启动。它是一个无限循环,持续检查并处理各种事件源。
事件循环基本工作原理

事件循环负责管理多个事件队列:
- 系统事件队列:存放来自操作系统的原始消息(如鼠标点击、键盘输入)
- QT事件队列 (Posted Events) :通过
QCoreApplication::postEvent()
放入,异步执行 - 发送事件队列 (Sent Events) :通过
QCoreApplication::sendEvent()
放入,同步执行
事件还拥有不同的优先级 (如Qt::HighEventPriority
、Qt::NormalEventPriority
、Qt::LowEventPriority
),高优先级事件会"插队"处理。
2. QEvent 类层次结构
QEvent
是所有事件的基类,Qt提供了丰富的事件子类用于不同场景:
事件类型 | 用途 | 典型应用场景 |
---|---|---|
QMouseEvent |
鼠标操作 | 点击、移动、拖拽 |
QKeyEvent |
键盘输入 | 按键检测、快捷键 |
QPaintEvent |
绘图事件 | 界面重绘 |
QTimerEvent |
定时器 | 定时任务、动画 |
QCloseEvent |
关闭事件 | 窗口关闭处理 |
QCustomEvent |
自定义事件 | 应用程序特定逻辑 |
3. 事件投递方式对比
Qt提供了两种主要的事件投递方式:
特性 | postEvent() | sendEvent() |
---|---|---|
执行方式 | 异步 | 同步 |
线程安全 | 是 | 否 |
内存管理 | Qt自动删除事件对象 | 需要手动管理 |
适用场景 | 跨线程通信、延迟处理 | 立即处理、同一线程内 |
4. 自定义事件创建与处理
创建自定义事件需要以下步骤:
- ① 定义自定义事件类型
- ② 创建自定义事件类
- ③ 投递自定义事件
- ④ 处理自定义事件
事件队列的实现依赖于以下几个关键组件:
- QEvent类:代表所有类型的事件
- QCoreApplication::postEvent():将事件投递到事件队列
- QEventLoop:管理事件循环的过程
- QTimer:提供定时事件支持
cpp
#include <QApplication>
#include <QDebug>
#include <QTimer>
// 创建自定义事件类
class CustomEvent : public QEvent
{
public:
static const QEvent::Type Type = static_cast<QEvent::Type>(1000); // 定义自定义事件类型
CustomEvent(const QString& message)
: QEvent(Type)
, m_message(message)
{}
QString message() const { return m_message; }
private:
QString m_message;
};
// 事件接收器 // 处理自定义事件
class EventReceiver : public QObject
{
protected:
bool event(QEvent* event) override
{
if (event->type() == CustomEvent::Type) {
CustomEvent* customEvent = static_cast<CustomEvent*>(event);
qDebug() << "Received event:" << customEvent->message();
return true;
}
return QObject::event(event);
}
};
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
EventReceiver receiver;
// 投递自定义事件到事件队列 // // 异步投递 - 线程安全 // 注意:事件对象必须用new创建,Qt会自动删除
QCoreApplication::postEvent(&receiver, new CustomEvent("Hello Event Queue!"));
// 使用定时器触发事件
QTimer::singleShot(1000, []() { qDebug() << "此处用于实现定时器事件处理"; }); // 1秒后触发
return app.exec();
}
cpp
Received event: "Hello Event Queue!"
此处用于实现定时器事件处理
三、多线程任务队列
📌 核心定位
Qt提供了多种机制实现多线程任务队列,用于将耗时的操作转移到后台线程执行,保持UI线程的响应性。
🔑 核心功能
- QtConcurrent命名空间:提供高级API实现并行计算
- QThreadPool类:管理可重用的线程池
- QRunnable类:代表可执行的任务单元
cpp
#include <QtConcurrent>
#include <QThreadPool>
#include <QDebug>
void processImage(const QString& imagePath) {
// 模拟耗时图像处理
qDebug() << "Processing image:" << imagePath << "in thread:" << QThread::currentThreadId();
QThread::sleep(1);
}
int main() {
// 获取全局线程池
QThreadPool* pool = QThreadPool::globalInstance();
qDebug() << "Max threads:" << pool->maxThreadCount();
QStringList imageList = {"image1.jpg", "image2.png", "image3.bmp", "image4.tiff"};
// 使用QtConcurrent运行任务
QtConcurrent::run(processImage, imageList[0]);
// 使用QThreadPool执行任务
class ImageTask : public QRunnable {
public:
ImageTask(const QString& path) : m_path(path) {}
void run() override {
processImage(m_path);
}
private:
QString m_path;
};
for (int i = 1; i < imageList.size(); ++i) {
pool->start(new ImageTask(imageList[i]));
}
// 等待所有任务完成
pool->waitForDone();
return 0;
}
四、线程安全队列实现
📌 核心定位
在多线程环境中,线程安全是队列实现的关键考虑因素。Qt提供了多种同步机制来确保多线程环境下数据访问的安全性。
🔑 核心功能
实现线程安全队列需要以下组件:
- QMutex:提供互斥锁机制
- QWaitCondition:允许线程在特定条件下等待
- QMutexLocker:简化互斥锁的管理
cpp
#include <QQueue>
#include <QMutex>
#include <QWaitCondition>
#include <QSharedPointer>
template<typename T>
class ThreadSafeQueue {
public:
void enqueue(const T& value) {
QMutexLocker locker(&m_mutex);
m_queue.enqueue(value);
m_condition.wakeOne(); // 唤醒一个等待线程
}
T dequeue() {
QMutexLocker locker(&m_mutex);
// 使用while而不是if来防止虚假唤醒
while (m_queue.isEmpty()) {
m_condition.wait(&m_mutex);
}
return m_queue.dequeue();
}
bool isEmpty() const {
QMutexLocker locker(&m_mutex);
return m_queue.isEmpty();
}
int size() const {
QMutexLocker locker(&m_mutex);
return m_queue.size();
}
private:
mutable QMutex m_mutex;
QWaitCondition m_condition;
QQueue<T> m_queue;
};
// 使用示例:图像处理管道
class ImageProcessor : public QObject {
Q_OBJECT
public:
void addImage(const QImage& image) {
m_queue.enqueue(image);
}
void startProcessing() {
QThread* thread = QThread::create([this]() {
while (!m_stopRequested) {
QImage image = m_queue.dequeue();
// 处理图像
processImage(image);
QThread::msleep(50);
}
});
thread->start();
}
void stopProcessing() {
m_stopRequested = true;
}
private:
ThreadSafeQueue<QImage> m_queue;
bool m_stopRequested = false;
};
五、队列应用场景与实战案例
📌 核心定位
队列在Qt开发中有多种实际应用场景,从简单的数据缓冲到复杂的系统架构。
🔑 核心功能
- 任务调度系统:使用队列管理待处理任务
- 数据缓冲:处理生产者-消费者速度不匹配问题
- 事件处理:管理GUI事件和异步操作
- 算法实现:广度优先搜索等算法的基础
实战案例:图像批处理系统
cpp
#include <QQueue>
#include <QImage>
#include <QDir>
#include <QDebug>
class ImageBatchProcessor : public QObject {
Q_OBJECT
public:
void loadImagesFromDirectory(const QString& directoryPath) {
QDir directory(directoryPath);
QStringList filters{"*.png", "*.jpg", "*.bmp"};
QStringList imageFiles = directory.entryList(filters, QDir::Files);
for (const QString& filename : imageFiles) {
QString fullPath = directory.filePath(filename);
QImage image(fullPath);
if (!image.isNull()) {
m_imageQueue.enqueue(image);
qDebug() << "Loaded image:" << fullPath;
}
}
emit imagesLoaded(m_imageQueue.size());
}
void processImages() {
while (!m_imageQueue.isEmpty()) {
QImage image = m_imageQueue.dequeue();
QTime startTime = QTime::currentTime();
// 执行图像处理操作
QImage processedImage = processSingleImage(image);
int processingTime = startTime.msecsTo(QTime::currentTime());
qDebug() << "Image processed in" << processingTime << "ms";
emit imageProcessed(processedImage);
}
emit processingFinished();
}
signals:
void imagesLoaded(int count);
void imageProcessed(const QImage& image);
void processingFinished();
private:
QQueue<QImage> m_imageQueue;
QImage processSingleImage(const QImage& image) {
// 这里实现具体的图像处理算法
// 例如: 垂直翻转图像
return image.mirrored(false, true);
}
};
六、队列性能优化与最佳实践
📌 核心定位
合理使用和优化队列对提升应用程序性能至关重要。
🔑 核心功能
- 内存预分配:对于可预测大小的队列,提前分配内存
- 移动语义:使用C++11移动语义减少拷贝开销
- 批量处理:减少锁竞争和提高缓存效率
- 队列大小监控:防止内存无限增长
cpp
#include <QQueue>
#include <QVector>
template<typename T>
class OptimizedQueue {
public:
OptimizedQueue(int initialCapacity = 1000) {
m_queue.reserve(initialCapacity);
}
void enqueue(const T& value) {
QMutexLocker locker(&m_mutex);
m_queue.enqueue(value);
}
void enqueue(T&& value) {
QMutexLocker locker(&m_mutex);
m_queue.enqueue(std::move(value)); // 使用移动语义
}
// 批量入队,减少锁竞争
void enqueueBatch(const QVector<T>& values) {
QMutexLocker locker(&m_mutex);
for (const T& value : values) {
m_queue.enqueue(value);
}
m_condition.wakeAll(); // 唤醒所有等待线程
}
// 批量出队,提高处理效率
QVector<T> dequeueBatch(int maxSize = 10) {
QMutexLocker locker(&m_mutex);
QVector<T> result;
result.reserve(maxSize);
while (!m_queue.isEmpty() && result.size() < maxSize) {
result.append(m_queue.dequeue());
}
return result;
}
// 监控队列大小,防止内存溢出
bool isAboveThreshold(int threshold = 10000) const {
QMutexLocker locker(&m_mutex);
return m_queue.size() > threshold;
}
private:
mutable QMutex m_mutex;
QWaitCondition m_condition;
QQueue<T> m_queue;
};
七、不同类型队列对比汇总
📌 核心定位
Qt提供了多种队列实现方式,每种方式适用于不同的应用场景。
🔑 核心功能
以下是Qt中主要队列类型的对比表:
队列类型 | 特点 | 适用场景 | 性能特点 |
---|---|---|---|
QQueue | 基于QList,简单易用 | 单线程环境,数据管理 | O(1)入队出队操作 |
事件队列 | 集成于事件循环,自动管理 | GUI应用,异步处理 | 事件驱动,高效响应 |
QtConcurrent | 高级API,自动线程管理 | 并行计算,CPU密集型任务 | 多核优化,负载均衡 |
自定义线程安全队列 | 完全控制,可高度定制 | 多线程数据交换,复杂同步需求 | 依赖实现方式,可优化 |
选择指南:
- 简单数据存储:使用
QQueue
- GUI事件处理:依赖内置事件队列
- 并行计算:选择
QtConcurrent
- 复杂多线程同步:实现自定义线程安全队列
总结
队列是Qt框架中多功能且强大的工具,贯穿于数据管理、事件处理和多线程编程等多个核心领域。通过合理选择和使用适当的队列类型,开发者可以构建出高效、响应迅速且可靠的应用程序。
关键要点
- 选择合适的队列类型:根据具体需求选择最合适的队列实现
- 重视线程安全:在多线程环境中务必使用适当的同步机制
- 性能考虑:对于高性能场景,优化队列实现和内存使用
- 集成Qt生态系统:充分利用Qt提供的各种队列相关工具和框架
Qt队列机制的有效运用不仅能提升程序性能,还能增强代码的可维护性和可扩展性,是每个Qt开发者应该掌握的核心技能。