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() <lock();
m_cond->wait(m_mutex);
qDebug() <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() <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() enqueue(value);
qDebug() << "producer enqueue" <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所使用的是条件变量,所以需要注意没有竞争条件出现。

原创文章,作者:小蓝,如若转载,请注明出处:https://www.506064.com/n/283312.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
小蓝小蓝
上一篇 2024-12-22 08:07
下一篇 2024-12-22 08:07

相关推荐

  • 神经网络代码详解

    神经网络作为一种人工智能技术,被广泛应用于语音识别、图像识别、自然语言处理等领域。而神经网络的模型编写,离不开代码。本文将从多个方面详细阐述神经网络模型编写的代码技术。 一、神经网…

    编程 2025-04-25
  • Linux sync详解

    一、sync概述 sync是Linux中一个非常重要的命令,它可以将文件系统缓存中的内容,强制写入磁盘中。在执行sync之前,所有的文件系统更新将不会立即写入磁盘,而是先缓存在内存…

    编程 2025-04-25
  • Python安装OS库详解

    一、OS简介 OS库是Python标准库的一部分,它提供了跨平台的操作系统功能,使得Python可以进行文件操作、进程管理、环境变量读取等系统级操作。 OS库中包含了大量的文件和目…

    编程 2025-04-25
  • MPU6050工作原理详解

    一、什么是MPU6050 MPU6050是一种六轴惯性传感器,能够同时测量加速度和角速度。它由三个传感器组成:一个三轴加速度计和一个三轴陀螺仪。这个组合提供了非常精细的姿态解算,其…

    编程 2025-04-25
  • 详解eclipse设置

    一、安装与基础设置 1、下载eclipse并进行安装。 2、打开eclipse,选择对应的工作空间路径。 File -> Switch Workspace -> [选择…

    编程 2025-04-25
  • Python输入输出详解

    一、文件读写 Python中文件的读写操作是必不可少的基本技能之一。读写文件分别使用open()函数中的’r’和’w’参数,读取文件…

    编程 2025-04-25
  • Java BigDecimal 精度详解

    一、基础概念 Java BigDecimal 是一个用于高精度计算的类。普通的 double 或 float 类型只能精确表示有限的数字,而对于需要高精度计算的场景,BigDeci…

    编程 2025-04-25
  • nginx与apache应用开发详解

    一、概述 nginx和apache都是常见的web服务器。nginx是一个高性能的反向代理web服务器,将负载均衡和缓存集成在了一起,可以动静分离。apache是一个可扩展的web…

    编程 2025-04-25
  • C语言贪吃蛇详解

    一、数据结构和算法 C语言贪吃蛇主要运用了以下数据结构和算法: 1. 链表 typedef struct body { int x; int y; struct body *nex…

    编程 2025-04-25
  • Linux修改文件名命令详解

    在Linux系统中,修改文件名是一个很常见的操作。Linux提供了多种方式来修改文件名,这篇文章将介绍Linux修改文件名的详细操作。 一、mv命令 mv命令是Linux下的常用命…

    编程 2025-04-25

发表回复

登录后才能评论