Qt 中的队列解析
- [一、Qt 中的队列解析](#一、Qt 中的队列解析)
- 二、代码示例
一、Qt 中的队列解析
Qt 提供了多种机制来实现类似队列的功能,主要涉及以下几个方面:
-
数据结构队列:
QQueue<T>-
本质:
QQueue<T>是一个模板类,它实际上是QList<T>的一个便捷别名。它提供了先进先出(FIFO)的队列操作接口。 -
核心操作:
enqueue(const T &value): 将元素value添加到队列的尾部(入队)。T dequeue(): 移除并返回队列头部的元素(出队)。如果队列为空,行为未定义(通常会导致崩溃),使用前最好检查isEmpty()。T &head(): 返回队列头部元素的引用(不移除)。const T &head() const: 返回队列头部元素的常量引用(不移除)。T &first(): 同head()。const T &first() const: 同head() const。T &last(): 返回队列尾部元素的引用。const T &last() const: 返回队列尾部元素的常量引用。bool isEmpty() const: 检查队列是否为空。int size() const: 返回队列中元素的数量。
-
示例:
cpp#include <QQueue> #include <QDebug> int main() { QQueue<int> queue; // 入队 queue.enqueue(10); queue.enqueue(20); queue.enqueue(30); // 检查头部 qDebug() << "Head:" << queue.head(); // 输出 10 // 出队 int firstOut = queue.dequeue(); // firstOut = 10 qDebug() << "Dequeued:" << firstOut; // 再次检查头部 qDebug() << "Head now:" << queue.head(); // 输出 20 return 0; } -
注意:
QQueue本身不是线程安全 的。如果需要在多线程环境中使用队列,必须使用互斥锁(如QMutex或QReadWriteLock)进行保护。
-
-
跨线程通信队列:信号与槽的队列连接 (
Qt::QueuedConnection)-
概念: 这是 Qt 事件驱动和线程模型的核心机制之一。当信号通过队列连接 (
Qt::QueuedConnection) 方式连接到槽时,信号的发射并不会立即调用槽函数。 -
工作原理:
- 信号发射时,一个包含信号参数副本的事件(
QMetaCallEvent)被放入接收者对象所在线程的事件队列中。 - 接收者线程的事件循环 (
QEventLoop) 在后续的某个时刻(当它处理事件队列时)会取出这个事件。 - 事件循环根据事件中的信息调用对应的槽函数。
- 信号发射时,一个包含信号参数副本的事件(
-
特点:
- 异步执行: 槽函数在接收者线程中异步执行,与信号发射线程解耦。这保证了线程安全。
- 跨线程通信: 这是不同线程间对象通信的标准和安全方式。
- 事件驱动: 依赖于接收者线程正在运行的事件循环。
-
连接方式:
cppQObject::connect(sender, &Sender::signalName, receiver, &Receiver::slotName, Qt::QueuedConnection); // 显式指定队列连接- 当
sender和receiver位于不同线程,且没有显式指定连接类型时,默认的连接类型Qt::AutoConnection会被解释为Qt::QueuedConnection。
- 当
-
示例场景: 工作线程完成计算后,通过信号将结果发送到主线程的某个对象进行处理(如更新 UI)。
-
-
任务队列:
QThreadPool和QRunnable-
概念:
QThreadPool管理了一个线程池,用于执行可运行的任务 (QRunnable)。你可以将多个QRunnable对象提交给线程池,它们会被放入一个内部队列中。 -
工作原理:
- 用户创建继承自
QRunnable的子类,并重写run()方法定义任务逻辑。 - 创建该任务对象。
- 调用
QThreadPool::globalInstance()->start(runnable)将任务提交给全局线程池。 - 线程池中的空闲线程会从队列中取出任务并执行其
run()方法。 - 如果所有线程都忙,新提交的任务会排队等待,直到有线程空闲。
- 用户创建继承自
-
特点:
- 任务队列: 线程池内部维护了一个待执行任务的队列。
- 线程复用: 避免了频繁创建和销毁线程的开销。
- 并行执行: 允许多个任务在多个线程上并行执行(取决于线程池大小)。
- 管理: 可以设置全局线程池或创建私有线程池,并设置最大线程数等。
-
示例:
cpp#include <QRunnable> #include <QThreadPool> #include <QDebug> class MyTask : public QRunnable { public: void run() override { qDebug() << "Task running in thread:" << QThread::currentThread(); // 执行耗时操作... } }; int main() { MyTask *task1 = new MyTask(); MyTask *task2 = new MyTask(); // 提交任务到全局线程池队列 QThreadPool::globalInstance()->start(task1); QThreadPool::globalInstance()->start(task2); // 主线程可以继续执行其他操作... return 0; }
-
总结:
Qt 中的"队列"概念体现在几个层面:
QQueue<T>: 作为数据结构,提供 FIFO 操作。适用于单线程或受保护的多线程环境。- 信号与槽的
Qt::QueuedConnection: 作为跨线程通信机制 的核心。信号发射被转换为事件放入接收线程的事件队列,由事件循环异步处理。这是实现线程间安全通信的标准方式。 QThreadPool: 管理一个任务队列 。用户提交的QRunnable任务被排队,由线程池中的工作线程取出执行,实现任务的并行处理。
二、代码示例
cpp
#include <QCoreApplication>
#include <QQueue>
#include <QMutex>
#include <QMutexLocker>
#include <QThread>
#include <QDebug>
// 全局队列 + 互斥锁(多线程必须加锁)
QQueue<int> g_queue;
QMutex g_mutex;
// 生产者线程:往队列里放数据
class Producer : public QThread
{
protected:
void run() override
{
for (int i = 1; i <= 10; ++i)
{
// 自动加锁/解锁
QMutexLocker locker(&g_mutex);
g_queue.enqueue(i);
qDebug() << "生产:" << i << " 队列大小:" << g_queue.size();
// 模拟耗时
msleep(200);
}
}
};
// 消费者线程:从队列取数据
class Consumer : public QThread
{
protected:
void run() override
{
while (true)
{
QMutexLocker locker(&g_mutex);
if (!g_queue.isEmpty())
{
int val = g_queue.dequeue();
qDebug() << "消费:" << val << " 队列大小:" << g_queue.size();
}
else
{
// 队列为空,稍等再取
qDebug() << "队列为空,等待...";
}
msleep(300);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer p;
Consumer c;
p.start();
c.start();
p.wait();
c.wait();
return a.exec();
}
运行结果:
cpp
08:57:14: Starting G:\Learning\Qt\QJsonSchema\build\Desktop_Qt_6_9_0_MinGW_64_bit-Debug\debug\QJsonSchema.exe...
生产: 1 队列大小: 1
生产: 2 队列大小: 2
生产: 3 队列大小: 3
生产: 4 队列大小: 4
生产: 5 队列大小: 5
生产: 6 队列大小: 6
生产: 7 队列大小: 7
生产: 8 队列大小: 8
生产: 9 队列大小: 9
生产: 10 队列大小: 10
消费: 1 队列大小: 9
消费: 2 队列大小: 8
消费: 3 队列大小: 7
消费: 4 队列大小: 6
消费: 5 队列大小: 5
消费: 6 队列大小: 4
消费: 7 队列大小: 3
消费: 8 队列大小: 2
消费: 9 队列大小: 1
消费: 10 队列大小: 0
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...
队列为空,等待...

