您的位置:

QWaitCondition详解

一、QWaitCondition简介

QWaitCondition是Qt中的一个同步原语,它是一个条件变量(condition variable)对象,允许线程等待某些条件变为满足后再继续执行。在多线程编程中,条件变量经常与锁(mutex)配合使用,通常是用于实现生产者-消费者模式,读者-写者模式等。QWaitCondition类可以用来实现上述模式,而不需要线程轮询或者繁琐的信号机制。

QWaitCondition类通常与QMutex类一起使用,QMutex用于保护共享资源的临界区,QWaitCondition用于同步线程。QWaitCondition由两个方法来实现条件等待:

  • wait(QMutex *mutex, ulong time = ULONG_MAX)  — 等待条件变量,释放mutex锁。
  • wakeOne()  — 唤醒等待条件变量的一个线程。
  • wakeAll()  — 唤醒等待条件变量的所有线程。

二、QWaitCondition的使用

1. 简单使用

下面的例子展示了两个线程来访问共享变量,QWaitCondition用于同步这两个线程。在示例中,首先创建一个Waiter类来等待条件变量,然后创建一个Notifier类来发出条件变量信号。

Waiter.h

```cpp #ifndef WAITER_H #define WAITER_H #include #include #include class Waiter : public QThread { Q_OBJECT public: Waiter(QMutex* mutex, QWaitCondition* cond) : m_mutex(mutex), m_cond(cond) {} void run() override { qDebug() << "waiter start waiting"; m_mutex->lock(); m_cond->wait(m_mutex); qDebug() << "waiter finish waiting"; m_mutex->unlock(); } private: QMutex* m_mutex; QWaitCondition* m_cond; }; #endif // WAITER_H ```

Notifier.h

```cpp #ifndef NOTIFIER_H #define NOTIFIER_H #include #include #include class Notifier : public QThread { Q_OBJECT public: Notifier(QMutex* mutex, QWaitCondition* cond) : m_mutex(mutex), m_cond(cond) {} void run() override { m_mutex->lock(); QThread::sleep(1); qDebug() << "notifier wake one waiter up"; m_cond->wakeOne(); // 唤醒所有waiter等待线程 m_mutex->unlock(); } private: QMutex* m_mutex; QWaitCondition* m_cond; }; #endif // NOTIFIER_H ```

main函数

```cpp #include #include #include "Waiter.h" #include "Notifier.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMutex mutex; QWaitCondition cond; Waiter *waiter1 = new Waiter(&mutex, &cond); Waiter *waiter2 = new Waiter(&mutex, &cond); Notifier *notifier = new Notifier(&mutex, &cond); waiter1->start(); waiter2->start(); notifier->start(); waiter1->wait(); waiter2->wait(); notifier->wait(); delete waiter1; delete waiter2; delete notifier; return a.exec(); } ``` 在这个例子中,我们首先创建了一个Mutex和WaitCondition,然后创建了两个Waiter线程和一个Notifier线程。两个Waiter线程都在等待条件变量,等待Notifier线程发出条件变量信号后才会继续执行。Notifier线程可以通过调用wakeOne(或wakeAll)方法来唤醒等待线程。在释放锁之前,wait()会一直等待。

2. 复杂使用

这里使用一个模拟生产者和消费者的例子。主线程是消费者,生产者线程异步地生产数据,并在队列 ready 中放入数据,消费者线程在队列 ready 中取出数据并处理。

producer.h

```cpp #ifndef PRODUCER_H #define PRODUCER_H #include #include #include #include #include #include class Producer : public QThread { Q_OBJECT public: explicit Producer(QMutex *mutex, QWaitCondition *cond, QObject *parent = nullptr): QThread (parent), m_mutex(mutex), m_condition(cond), m_ready(nullptr), m_running(false) {} void run() override { QMutexLocker locker(m_mutex); m_running = true; while(m_running) { if(m_ready && m_ready->count() < 10) { qsrand(QTime(0, 0, 0).msecsTo(QTime::currentTime())); int value = qrand() % 100 + 1; m_ready->enqueue(value); qDebug() << "producer enqueue" << value; } // 模拟生产者生产的时间 lock_wait(100); m_condition->wakeAll(); } } void stop(void) {m_running = false;} void setReady(QQueue *ready) {m_ready = ready;} private: void lock_wait(int ms) { m_mutex->unlock(); QThread::msleep(ms); m_mutex->lock(); } private: QMutex *m_mutex; QWaitCondition *m_condition; QQueue *m_ready; bool m_running; }; #endif // PRODUCER_H ```

consumer.h

```cpp #ifndef CONSUMER_H #define CONSUMER_H #include #include #include #include #include #include class Consumer : public QThread { Q_OBJECT public: explicit Consumer(QMutex *mutex, QWaitCondition *cond, QObject *parent = nullptr): QThread (parent), m_mutex(mutex), m_condition(cond), m_ready(nullptr), m_running(false) {} void run() override { QMutexLocker locker(m_mutex); m_running = true; while(m_running) { while(m_ready && m_ready->count() == 0) { m_condition->wait(m_mutex); } int value = 0; while (m_ready && m_ready->count() > 0) { value = m_ready->dequeue(); qDebug() << "consumer dequeue" << value; } // 模拟消费者消费的时间 lock_wait(100); } } void setReady(QQueue *ready) {m_ready = ready;} void stop(void) {m_running = false;} private: void lock_wait(int ms) { m_mutex->unlock(); QThread::msleep(ms); m_mutex->lock(); } private: QMutex *m_mutex; QWaitCondition *m_condition; QQueue *m_ready; bool m_running; }; #endif // CONSUMER_H ```

main函数

```cpp #include #include #include "producer.h" #include "consumer.h" int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); QMutex mutex; QWaitCondition condition; QQueue ready; Producer *producer = new Producer(&mutex, &condition); Consumer *consumer = new Consumer(&mutex, &condition); producer->setReady(&ready); consumer->setReady(&ready); producer->start(); consumer->start(); QThread::sleep(5); producer->stop(); consumer->stop(); producer->wait(); consumer->wait(); delete producer; delete consumer; return a.exec(); } ``` 在这个例子中,我们有一个 Producer 线程通过异步的方式生成数据,并将它们存储在队列 ready 中。一个 Consumer 线程从队列 ready 中获取数据并处理。

结语

本文简要介绍了QWaitCondition的基本知识和使用方法。QWaitCondition可以与QMutex一起使用来实现线程同步,在多个线程之间进行通信。正如上述示例中的生产者-消费者模型,它可以帮助我们更轻松地编写多线程程序来实现复杂的操作。但是,在使用QWaitCondition时,我们需要注意避免死锁的问题。同时,由于QWaitCondition所使用的是条件变量,所以需要注意没有竞争条件出现。