面试问题详解十五:Qt 多线程同步【QWaitCondition】讲解
本文介绍了Qt中用于线程同步的QWaitCondition工具。它配合QMutex使用,实现线程间的条件等待和唤醒机制。文章通过生产者-消费者模型示例,展示了如何使用wait()和wakeOne()/wakeAll()方法进行线程协调。关键点包括:必须与互斥锁配合使用、条件判断应使用while循环防止虚假唤醒、多个条件变量需合理设计。这种机制适用于需要线程等待特定条件成立的场景,能有效提升系统资源
·
在多线程编程中,我们经常会遇到以下场景:
某个线程需要“等一等”,直到另一个线程完成某件事情,才继续往下执行。
此时,仅靠互斥锁(QMutex
)是不够的。我们需要一种机制来挂起线程并等待特定条件成立时被唤醒。
Qt 提供的解决方案就是 —— QWaitCondition
。
一、什么是 QWaitCondition?
QWaitCondition
是 Qt 中实现线程之间条件同步的工具。
它允许一个或多个线程挂起(等待),直到满足某个条件再继续执行。常配合 QMutex
使用,用于安全地等待和通知。
简要理解:
QMutex
用于互斥访问共享数据QWaitCondition
用于等待某个状态发生变化
二、典型应用场景
- 生产者-消费者模型:消费者线程等待有数据可读,生产者生产数据后唤醒消费者
- 线程同步:某些线程需要等待其他线程完成特定任务后才能继续执行
- 任务协调:多个线程间的阶段协调,比如等所有任务准备好再一起执行
三、核心方法说明
方法 | 说明 |
---|---|
wait(QMutex *mutex) |
当前线程挂起并释放互斥锁,直到被 wakeOne() 或 wakeAll() 唤醒 |
wait(QMutex *mutex, long timeout) |
设置超时时间,超时后自动恢复继续执行 |
wakeOne() |
唤醒一个正在等待的线程 |
wakeAll() |
唤醒所有等待的线程 |
四、完整示例:生产者-消费者模型
我们通过一个经典的“生产者-消费者”模型,来演示 QWaitCondition
的使用。
设计说明:
- 有一个共享缓冲区(假设只允许保存一条数据)
- 生产者线程:不断生成数据,放入缓冲区;若缓冲区非空,则等待
- 消费者线程:不断从缓冲区读取数据;若缓冲区为空,则等待
C++ 示例代码(Qt Console 项目)
#include <QCoreApplication>
#include <QThread>
#include <QMutex>
#include <QWaitCondition>
#include <QDebug>
QString buffer;
bool hasData = false;
QMutex mutex;
QWaitCondition bufferNotEmpty;
QWaitCondition bufferNotFull;
class Producer : public QThread
{
public:
void run() override {
for (int i = 1; i <= 5; ++i) {
mutex.lock();
while (hasData) {
bufferNotFull.wait(&mutex); // 等待缓冲区为空
}
buffer = QString("数据 %1").arg(i);
qDebug() << "生产者:生成" << buffer;
hasData = true;
bufferNotEmpty.wakeOne(); // 通知消费者
mutex.unlock();
QThread::sleep(1);
}
}
};
class Consumer : public QThread
{
public:
void run() override {
for (int i = 1; i <= 5; ++i) {
mutex.lock();
while (!hasData) {
bufferNotEmpty.wait(&mutex); // 等待缓冲区非空
}
qDebug() << "消费者:消费" << buffer;
hasData = false;
bufferNotFull.wakeOne(); // 通知生产者
mutex.unlock();
QThread::sleep(2);
}
}
};
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
Producer producer;
Consumer consumer;
producer.start();
consumer.start();
producer.wait();
consumer.wait();
qDebug() << "数据处理完毕!";
return 0;
}
程序输出(示例)
生产者:生成 数据 1
消费者:消费 数据 1
生产者:生成 数据 2
消费者:消费 数据 2
...
数据处理完毕!
五、代码详解
- 共享资源与同步变量
QString buffer;
bool hasData = false;
QMutex mutex;
QWaitCondition bufferNotEmpty, bufferNotFull;
buffer
:共享数据区域hasData
:标记缓冲区是否有数据mutex
:保护数据访问安全bufferNotEmpty
和bufferNotFull
:分别表示缓冲区“有数据”和“有空间”的条件
- 生产者逻辑
while (hasData) {
bufferNotFull.wait(&mutex);
}
- 如果当前缓冲区有数据,生产者就阻塞等待
- 当消费者消费后调用
wakeOne()
唤醒它
- 消费者逻辑
while (!hasData) {
bufferNotEmpty.wait(&mutex);
}
- 如果缓冲区为空,消费者就阻塞等待
- 当生产者生产完数据后调用
wakeOne()
唤醒消费者
六、QWaitCondition 使用注意事项
wait()
必须和QMutex
配合使用,且必须先加锁再调用wait()
会自动释放互斥锁,并在被唤醒后重新加锁- 条件判断应放在
while
循环中,而不是if
,防止虚假唤醒(spurious wakeups) - 若涉及多个条件变量,建议清晰命名、合理拆分逻辑,避免混淆
七、适用建议与扩展
- 可以使用多个
QWaitCondition
来控制不同的条件等待 - 如果多个线程都在等待同一个条件发生,使用
wakeAll()
比wakeOne()
更合适 - 配合
QSemaphore
可以构建更复杂的限流+等待逻辑 - 在 GUI 程序中慎用线程阻塞,避免界面卡顿
八、总结
QWaitCondition
是 Qt 提供的功能强大且灵活的线程间同步工具,适合用于线程之间“等待-通知”机制的场景。它可以让线程在没有满足条件之前主动进入“睡眠”,从而提升系统资源利用率。
关键用法可以总结为:
- 使用前先加锁(
mutex.lock()
) - 使用
wait(&mutex)
进行阻塞 - 使用
wakeOne()
或wakeAll()
进行唤醒 - 条件判断使用
while
包裹,防止虚假唤醒
更多推荐
所有评论(0)