在多线程编程中,我们经常会遇到以下场景:

某个线程需要“等一等”,直到另一个线程完成某件事情,才继续往下执行。

此时,仅靠互斥锁(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
...
数据处理完毕!

五、代码详解

  1. 共享资源与同步变量
QString buffer;
bool hasData = false;
QMutex mutex;
QWaitCondition bufferNotEmpty, bufferNotFull;
  • buffer:共享数据区域
  • hasData:标记缓冲区是否有数据
  • mutex:保护数据访问安全
  • bufferNotEmptybufferNotFull:分别表示缓冲区“有数据”和“有空间”的条件
  1. 生产者逻辑
while (hasData) {
    bufferNotFull.wait(&mutex);
}
  • 如果当前缓冲区有数据,生产者就阻塞等待
  • 当消费者消费后调用 wakeOne() 唤醒它
  1. 消费者逻辑
while (!hasData) {
    bufferNotEmpty.wait(&mutex);
}
  • 如果缓冲区为空,消费者就阻塞等待
  • 当生产者生产完数据后调用 wakeOne() 唤醒消费者

六、QWaitCondition 使用注意事项

  • wait() 必须和 QMutex 配合使用,且必须先加锁再调用
  • wait() 会自动释放互斥锁,并在被唤醒后重新加锁
  • 条件判断应放在 while 循环中,而不是 if,防止虚假唤醒(spurious wakeups)
  • 若涉及多个条件变量,建议清晰命名、合理拆分逻辑,避免混淆

七、适用建议与扩展

  • 可以使用多个 QWaitCondition 来控制不同的条件等待
  • 如果多个线程都在等待同一个条件发生,使用 wakeAll()wakeOne() 更合适
  • 配合 QSemaphore 可以构建更复杂的限流+等待逻辑
  • 在 GUI 程序中慎用线程阻塞,避免界面卡顿

八、总结

QWaitCondition 是 Qt 提供的功能强大且灵活的线程间同步工具,适合用于线程之间“等待-通知”机制的场景。它可以让线程在没有满足条件之前主动进入“睡眠”,从而提升系统资源利用率。

关键用法可以总结为:

  1. 使用前先加锁(mutex.lock()
  2. 使用 wait(&mutex) 进行阻塞
  3. 使用 wakeOne()wakeAll() 进行唤醒
  4. 条件判断使用 while 包裹,防止虚假唤醒

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐