QWaitCondition 条件变量

本文介绍了Qt中的QWaitCondition条件变量,作为线程同步的一种方式,它允许线程在特定条件满足时被唤醒。通过示例展示了如何在生产者-消费者问题中使用QWaitCondition和QMutex进行配合,实现线程间的协调通信。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在之前的文章中,我们已经讲过了很多种线程同步的方法,如互斥锁,信号量,读写锁等,今天我们再来学习一种线程同步的方法,条件变量。

条件变量允许一个线程通知其他的线程它们所等待的某个条件已经满足了,可以继续运行了。一个或多个线程可以在同一个条件变量上等待。当条件满足时,我们可以调用wakeOne()从所有等待在该条件变量上的线程中随机的唤醒一个线程继续运行,也可以使用wakeAll()方法同时唤醒所有等待在该条件变量上的线程。

另外,QWaitCondition和QSemaphore一样,要和QMutex配合使用,因为要访问到共享资源。

下面,我们使用常见的生产者-消费者问题来演示一下条件变量的使用。

新建一个Qt控制台程序,再新建两个线程类Producer和Consumer,继承自QThread类。

我们先在main.cpp中,声明我们要用到的全局变量。代码如下:

#include <QCoreApplication>
#include <QWaitCondition>
#include <QMutex>
#include <QQueue>

QQueue<int> buffer;
QMutex mutex;
QWaitCondition fullCond;
QWaitCondition emptyCond;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    return a.exec();
}
其中,buffer就是我们的缓冲区,mutex是保护缓存区的互斥锁,条件变量fullCond是用来等待缓冲区变满(有数据),条件变量emptyCond是用来等待缓冲区变为空。

下面来实现我们的消费者和生产者线程:

生产者代码如下:

void Producer::run()
{
    while(true)
    {
        mutex.lock();
        while(buffer.size() >= 10)
        {
            emptyCond.wait(&mutex);
        }
        int num = rand();
        buffer.enqueue(num);
        qDebug() << "enqueue: " << num;
        mutex.unlock();
        fullCond.wakeAll();
    }
}
此处,我们假定缓冲区最多存储10个元素。所以,我们先判断缓冲区是否已满,如果已满,则等待其变为空,否则,产生一个随机数放入队列中,最后通知消费者线程,可以进行消费。

消费者代码如下:

void Consumer::run()
{
    while(true)
    {
        mutex.lock();
        while(buffer.size() <= 0)
        {
            fullCond.wait(&mutex);
        }
        qDebug() << "dequeue: " << buffer.dequeue();
        mutex.unlock();
        emptyCond.wakeAll();
    }
}
对于消费者来说,要先判断缓冲区是否有数据可消费,如果没有,则等待其生产者生产出新的数据,如果有,则消费一个数据,最后,通知生产者继续生产。

运行结果如下:



可以看出,几乎是生产者生产一个数据,消费者就消费一个。如果想看到累积的效果,可以使用线程睡眠来实现,大家可以自行测试。

### 条件变量Qt 中的使用方法及注意事项 Qt 提供了跨平台的多线程支持,其中条件变量(`QWaitCondition`)是实现线程间同步的重要工具之一。`QWaitCondition` 允许一个或多个线程等待某个特定条件的发生,常与 `QMutex` 或 `QMutexLocker` 配合使用,以确保线程安全性和避免竞态条件。 #### 使用方法 在 Qt使用 `QWaitCondition` 的基本流程如下: 1. 定义并初始化一个互斥锁(`QMutex`)和一个条件变量(`QWaitCondition`)。 2. 等待线程调用 `QWaitCondition::wait()`,在调用前必须先锁定互斥锁。 3. 当条件满足时,通知线程调用 `QWaitCondition::wakeOne()` 或 `QWaitCondition::wakeAll()` 唤醒等待线程。 4. 等待线程被唤醒后重新获取互斥锁并检查条件是否满足。 以下是一个简单的示例代码: ```cpp #include <QThread> #include <QWaitCondition> #include <QMutex> #include <QDebug> QWaitCondition condition; QMutex mutex; bool isReady = false; class WorkerThread : public QThread { public: void run() override { mutex.lock(); while (!isReady) { condition.wait(&mutex); } qDebug() << "Worker thread: condition met"; mutex.unlock(); } }; int main() { WorkerThread thread; thread.start(); // 模拟延迟 QThread::sleep(1); mutex.lock(); isReady = true; condition.wakeOne(); mutex.unlock(); thread.wait(); return 0; } ``` #### 注意事项 - **避免虚假唤醒**:即使没有显式调用 `wakeOne()` 或 `wakeAll()`,线程也可能被唤醒。因此在调用 `wait()` 后应再次检查条件是否满足,通常使用 `while` 循环来确保条件成立[^1]。 - **始终在互斥锁保护下操作条件变量**:所有对共享状态的访问都应在互斥锁保护下进行,以防止多个线程同时修改共享数据导致的数据竞争。 - **使用 `wakeOne()` 和 `wakeAll()` 的区别**:`wakeOne()` 唤醒一个等待的线程,适用于生产者-消费者模型中单个任务被处理的情况;而 `wakeAll()` 唤醒所有等待的线程,适用于多个线程需要响应同一条件变化的场景。 - **避免死锁**:确保等待线程在调用 `wait()` 前已正确锁定互斥锁,通知线程在修改条件后释放互斥锁,以防止死锁的发生。 - **合理选择唤醒策略**:根据实际需求选择 `wakeOne()` 或 `wakeAll()`,避免不必要的资源消耗。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值