Qt QQueue 安全的多线程队列、阻塞队列

news2024/12/25 10:04:56

文章目录

  • 1. C++ queue 队列基本用法
  • 2. Qt QQueue 队列基本用法
  • 3. Qt QQueue 多线程队列
  • 4. Qt BlockingQueue 自定义线程安全的阻塞队列

1. C++ queue 队列基本用法

在C++中,queue是一个模板类,用于实现队列数据结构,遵循先进先出的原则。

♦ 常用方法: ·

queue<int> Q;            //定义一个int型队列
Q.empty();               //返回队列是否为空
Q.size();                //返回当前队列长度
Q.front();               //返回当前队列的第一个元素
Q.back();              	 //返回当前队列的最后一个元素
Q.push();                //在队列后面插入一个元素, 比如插入数字5: Q.push(5)
Q.pop();                 //从当前队列里,移出第一个元素

♦ 简单使用: ·

#include <iostream>
#include <queue>

using namespace std;

int main()
{
    // 创建一个queue对象
    queue<int> Q;

    // 向队列中添加元素
    Q.push(1);
    Q.push(2);
    Q.push(3);

    cout<<"queue empty?  "<<Q.empty()<<endl;
    cout<<"queue size:   "<<Q.size()<<endl;

    // 从队列中移除元素,并输出
    while (!Q.empty()) {
        int value = Q.front();
        Q.pop();
        cout << "Dequeued:" << value << endl;
    }

    return 0;
}

♦ 打印: ·

在这里插入图片描述

2. Qt QQueue 队列基本用法

QQueue 继承与 QList

♦ 常用方法: ·

QQueue<int> QQ;         	 //定义一个int型队列

QQ.isEmpty();                //返回队列是否为空

QQ.size();                   //返回队列元素个数

QQ.clear();                  //清空队列

QQ.enqueue();                //在队列尾部添加一个元素, 比如插入数字5: QQ.enqueue(5)
/* 相当于
Q.push();                 
*/

QQ.dequeue();                //删除当前队列第一个元素,并返回这个元素
/* 相当于
Q.front();                   //返回当前队列的第一个元素
Q.pop();                     //从当前队列里,移出第一个元素
*/

QQ.head();                   //返回当前队列第一个元素
/* 相当于
Q.front();                   
*/

QQ.last();                   //返回当前队列尾部的元素
/* 相当于
Q.back();              	   
*/
T &  operator[]( int i );   //以数组形式访问队列元素

♦ 实例: ·

#include <QCoreApplication>
#include <QQueue>
#include <QDebug>

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

    // 创建一个QQueue对象
    QQueue<int> QQ;

    // 向队列中添加元素
    QQ.enqueue(1);
    QQ.enqueue(2);
    QQ.enqueue(3);

    qDebug()<<"queue empty:  "<<QQ.isEmpty();
    qDebug()<<"queue size:  " <<QQ.size();
    qDebug()<<"queue head:  " <<QQ.head()  ;
    qDebug()<<"queue last:  " <<QQ.last()  << "\n";


    // 从队列中移除元素,并输出
    while (!QQ.isEmpty()) {
        int value = QQ.dequeue();
        qDebug() << "Dequeued:" << value;
    }

    return a.exec();
}

♦ 打印: ·

在这里插入图片描述

3. Qt QQueue 多线程队列

在多线程编程中,由于QQueue并不是线程安全的,因此我们需要先使用互斥锁(QMutex)来保护队列。在读写队列时,我们需要获取互斥锁的锁定,以避免多个线程同时访问队列导致的数据竞争问题。

然后通过经典的生产者和消费者来看一个简单的示例程序,演示如何使用QQueue实现线程安全队列:

#include <QCoreApplication>
#include <QQueue>
#include <QMutex>
#include <QThread>
#include <QDebug>

// 定义线程安全队列类
template<typename T>
class ThreadSafeQueue
{
public:
    // 添加元素到队列尾部
    void enqueue(const T& value) {
        QMutexLocker locker(&m_mutex);
        m_queue.enqueue(value);
    }

    // 从队列头部移除一个元素,并返回它
    T dequeue() {
        QMutexLocker locker(&m_mutex);
        if (m_queue.isEmpty()) {
            return T();
        }
        return m_queue.dequeue();
    }

    // 返回队列是否为空
    bool isEmpty() const {
        QMutexLocker locker(&m_mutex);
        return m_queue.isEmpty();
    }

private:
    QQueue<T> m_queue;
    mutable QMutex m_mutex;
};

// 定义生产者线程类
class ProducerThread : public QThread
{
public:
    ProducerThread(ThreadSafeQueue<int>& queue)
        : m_queue(queue)
    {
    }

protected:
    void run() override {
        for (int i = 1; i <= 10; i++) {
            m_queue.enqueue(i);
            qDebug() << "Enqueued:" << i;
            msleep(500);
        }
    }

private:
    ThreadSafeQueue<int>& m_queue;
};

// 定义消费者线程类
class ConsumerThread : public QThread
{
public:
    ConsumerThread(ThreadSafeQueue<int>& queue)
        : m_queue(queue)
    {
    }

protected:
    void run() override {
        while (!isInterruptionRequested()) {
            if (!m_queue.isEmpty()) {
                int value = m_queue.dequeue();
                qDebug() << "Dequeued:" << value;
            }
            msleep(500);
        }
    }

private:
    ThreadSafeQueue<int>& m_queue;
};

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

    // 创建线程安全队列对象
    ThreadSafeQueue<int> queue;

    // 创建生产者线程对象和消费者线程对象
    ProducerThread producer(queue);
    ConsumerThread consumer(queue);

    // 启动线程
    producer.start();
    consumer.start();

    qDebug() << "F1";

    // 等待线程结束
    //在producer等待期间,  consumer run()也会运行 直到producer 运行完毕,main才能往下执行
    producer.wait();

    qDebug() << "F2";

    consumer.requestInterruption(); //相当于 ctrl + c 结束 consumer 线程
    qDebug() << "F3";
    consumer.wait();
    qDebug() << "F4";

    return a.exec();
}

♦ 运行结果:

在这里插入图片描述

在上面的示例程序中,我们首先定义了一个模板类ThreadSafeQueue,用于实现线程安全队列。该类使用QMutex来保护QQueue对象,以实现线程安全。

接下来,我们定义了两个线程类ProducerThread和ConsumerThread,用于生产和
消费数据。

在ProducerThread中,我们循环向队列中添加元素,每隔500毫秒添加一个元素。在ConsumerThread中,我们循环从队列中取出元素,每隔500毫秒取出一个元素。在取出元素时,我们需要判断队列是否为空,避免出现异常情况。

其中:执行 producer.wait() 时, mian 中主线程将会被阻塞; 等到 producer 生产者运行完毕,才会唤醒;而 consumer 线程不受影响;

万一发生数据处理速度不匹配的情况呢?

  • 生产者休眠 500ms 消费者休眠500ms, 就是如上情况
  • 生产者休眠时间 < 消费者休眠时间, 那么生产者执行完毕后,消费者还未消费完就退出线程了,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕
  • 生产者休眠时间 > 消费者休眠时间, 那么生产者执行完毕后,消费者也执行完毕了

真实的大数据情况下,如果生产者产出数据的速度大于消费者消费的速度,并且当生产出来的数据累积到一定程度的时候,那么生产者必须暂停等待一下(阻塞生产者线程),以便等待消费者线程把累积的数据处理完毕,反之亦然。在多线程环境下,我们每个程序员都必须去自己控制这些细节,尤其还要兼顾效率和线程安全,而这会给我们的程序带来不小的复杂度。

在 java 中有 BlockingQueue 阻塞队列,但是Qt 中似乎没有相关的阻塞队列,需要我们自己去自己控制这些细节

4. Qt BlockingQueue 自定义线程安全的阻塞队列

在这里插入图片描述

以下定义一个简单的阻塞队列:

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

template <typename T>
class BlockingQueue
{
public:
    BlockingQueue() {}
    void put(const T& value)
    {
        QMutexLocker locker(&m_mutex);
        m_queue.enqueue(value);
        m_condition.wakeOne();   //唤醒等待队列中的一个线程(来自wait)
    }
    T take()
    {
        QMutexLocker locker(&m_mutex);
        while (m_queue.isEmpty()) {
            m_condition.wait(&m_mutex);
        }
        return m_queue.dequeue();
    }
    bool isEmpty() const
    {
        QMutexLocker locker(&m_mutex);
        return m_queue.isEmpty();
    }
    int size() const
    {
        QMutexLocker locker(&m_mutex);
        return m_queue.size();
    }

private:
    QQueue<T> m_queue;
    mutable QMutex m_mutex;
    QWaitCondition m_condition;
};

这个 BlockingQueue类使用QMutex和QWaitCondition来保证线程安全,并实现了put、take、isEmpty和size等方法。其中,put方法用于往队列中插入元素,take方法用于从队列中取出元素,isEmpty方法用于判断队列是否为空,size方法用于获取队列中元素的数量。

在put方法中,我们首先获取了互斥锁,然后将元素插入到队列中,并通过QWaitCondition的wakeOne()方法唤醒一个等待线程(调用take()中的线程)。在take方法中,我们首先获取了互斥锁,然后在队列为空时调用QWaitCondition的wait()方法等待,直到有其他线程往队列中插入了元素并唤醒了当前线程。

mutable的作用是允许在const成员函数中修改BlockingQueue类的m_mutex和m_notEmpty成员变量。这是因为,生产者和消费者线程在往阻塞队列中添加或删除元素时,都需要对这两个成员变量进行修改。但是,由于take()和tryTake()方法都是const成员函数,因此如果不将m_mutex和m_notEmpty声明为mutable类型,编译器就会报错。

♦ 使用: ·

static BlockingQueue<int> queue;

class Producer : public QThread
{
public:
    void run() override
    {
        for (int i = 0; i < 10; ++i) {
            queue.put(i);
            qDebug() << "Producer thread: " << QThread::currentThreadId() << ", value: " << i;
            msleep(500); // sleep for 0.5 second

        }
    }
};

class Consumer : public QThread
{
public:
    void run() override
    {
        int value = 0;
        while (true) {
            value = queue.take();
            qDebug() << "Consumer thread: " << QThread::currentThreadId() << ", value: " << value;
        }
    }
};

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

    Producer producer;
    Consumer consumer1, consumer2;

    producer.start();
    consumer1.start();
    consumer2.start();

    return a.exec();
}

♦ 运行结果:

在这里插入图片描述

上述包含一个生产者线程和两个消费者线程,生产者线程往队列中插入10个整数,每插入一个元素后暂停0.5秒。两个消费者线程不断从队列中取出元素,并输出当前线程的ID和取出的元素值。 当队列为空时,消费者线程会进入等待状态,直到有其他线程往队列中插入元素。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/499963.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

测试3:用例

目录 1.测试用例的基本要素 2.测试用例的设计方法 1.基于需求的设计方法 2.等价类 1.概念 2.步骤: 3.例子 3.边界值 1.概念 2.步骤 3.例子 4.判定表 1.概念 2.设计测试用例 3.例子 5.正交排列 1.什么是正交表 2.测试用例 3.如何通过正交表设计测试用例 6.场景…

(3)Qt——信号槽

目录 1.信号槽的概念** 2.信号槽的连接*** 2.1自带信号 → 自带槽 2.2 自带信号 → 自定义槽 2.3 自定义信号 3. 参数传递** 3.1 全局变量 3.2 信号槽传参 4. 对应关系** 4.1 一对多 4.2 多对一 1.信号槽的概念** 信号槽指的是信号函数与槽函数的连接&#xff0c;可…

AI绘图入门 安装 stable-diffusion-webui

下面介绍了N卡&#xff0c;A卡&#xff0c;或CPU跑 stable-diffusion-webui的方法。 1.安装python 3.10.x https://www.python.org/downloads/ 2.安装Git https://git-scm.com/downloads 【非必要】打开代理工具&#xff08;比如clash&#xff09;然后在cmd配置git的http和…

软件测试相关概念

✏️作者&#xff1a;银河罐头 &#x1f4cb;系列专栏&#xff1a;JavaEE &#x1f332;“种一棵树最好的时间是十年前&#xff0c;其次是现在” 目录 需求需求的定义测试人员眼中的需求为什么需求对测试人员如此重要如何深入理解需求 测试用例定义为什么要有测试用例 软件错误…

IT服务规划设计笔记

规划设计处于整个IT服务生命周期中的前端&#xff0c;其主要目的在于&#xff1a; &#xff08;1&#xff09;设计满足业务需求的IT服务 &#xff08;2&#xff09;设计SLA、测量方法和指标 &#xff08;3&#xff09;设计服务过程及其控制方法 &#xff08;4&#xff09;规…

learn_C_deep_9 (汇编角度理解return的含义、const 的各种应用场景)

return 关键字 不知道我们大家是否有一个疑惑&#xff1a;我们下载一个大型游戏软件&#xff08;王者荣耀&#xff09;&#xff0c;都要花几个小时去下载&#xff0c;但是一旦我们游戏连输&#xff0c;想要删除这个软件的时候&#xff0c;它仅仅只需要十几秒&#xff0c;这是为…

主题建模和文本聚类:理论与实践

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️ &#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

真题详解(3FN)-软件设计(六十九)

真题详解&#xff08;构造二叉树&#xff09;-软件设计&#xff08;六十八)https://blog.csdn.net/ke1ying/article/details/130536155 学生信息学生id姓名性别1{家长ID}*班主任班级。 解析&#xff1a;当存在1对多的情况&#xff0c;要写个1{}*&#xff0c;中间用{}。 ()表…

一篇学会Gitlab搭建及使用

目录 一、Gitlab介绍 1、什么是Gitlab 二、搭建gitlab并实现ssl 1、配置yum源或下载gitlab包 2、安装依赖软件及获取GPG密钥 3、安装gitlab-ce 4、创建私有密钥 5、创建私有证书 6、创建CRT签名证书 7、利用openssl签署pem 证书 8、配置证书到gitlab 9、初始化gitla…

读书笔记:《图解CIO工作指南》

《图解CIO工作指南》第 4 版&#xff0c;日 . 野村综合研究所系统咨询事业本部 著&#xff0c;周自恒 译 大数据、云计算时代下的IT战略和IT实务 CIO工作&#xff1a;IT管理、IT架构、IT实践 以着眼企业未来的观点进行构思&#xff1a;可视化&#xff08;业务与系统&am…

初始化vue中data中的数据

当组件的根元素使用了v-if的时候, 并不会初始化data中的数据 如果想完全销毁该组件并且初始化数据,需要在使用该组件的本身添加v-if 或者是手动初始化该组件中的数据 初始化化数据的一些方法 Object.assign(this.$data, this.$options.data()) this.$data&#xff1a;当前的da…

TortoiseGit(大乌龟)安装教程(Git 图形化工具,告别手敲命令)

TortoiseGit安装教程 1. 下载TortoiseGit 官方下载地址&#xff1a;https://tortoisegit.org/download/ 自行选择下载对应版本&#xff08;大部分位64位&#xff09;&#xff0c;进行下载 2. 安装TortoiseGit 打开安装包&#xff0c;如下图所示&#xff1a; 点击 Next&…

IDM绿色最新2023中文版磁力下载工具

Internet Download Manager&#xff08;idm&#xff09;是一款优秀的多线程下载工具。它支持自动捕获剪贴板及浏览器及流媒体网站的音视频下载链接&#xff0c;还有批量队列下载、静默下载、站点抓取等众多功能选项&#xff0c;可以说是 Windows 平台上功能最为强大的多线程下载…

通过栈/队列/优先级队列/了解容器适配器,仿函数和反向迭代器

文章目录 一.stack二.queue三.deque&#xff08;双端队列&#xff09;四.优先级队列优先级队列中的仿函数手搓优先级队列 五.反向迭代器手搓反向迭代器 vector和list我们称为容器&#xff0c;而stack和queue却被称为容器适配器。 这和它们第二个模板参数有关系&#xff0c;可以…

录屏界鼻祖Camtasia 2023中文版功能介绍/下载安装激活教程

随着网络科技的迅速发展&#xff0c;所以对于电脑的使用率也就越来越高了&#xff01;然而&#xff0c;也可能跟这有关系&#xff0c;目前各种类型的软件层出不穷&#xff0c;当然也就包括了电脑录屏软件。这给我们造成了一些困难&#xff0c;究竟哪一款适合自己呢&#xff1f;…

DMA的补充笔记

DMA有两个总线&#xff1a; 1、DMA存储器总线&#xff1a;DMA通过该总线来执行存储器数据的传入和传出。 2、DMA外设总线&#xff1a;DMA通过该总线访问AHB外设&#xff08;AHB主要是针对高效率、高频宽以及快速系统模块所设计的&#xff0c;主要有Flash 存储器、复位和时钟控…

栈和队列OJ题思路分享之栈和队列互换(C语言实现)

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:刷题分享⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你刷更多C语言和数据结构的题!   &#x1f51d;&#x1f51d; 栈和队列刷题分享二 1. 前言⚡…

基于人类反馈的强化学习(RLHF) 理论

gpt 进程 GPT-1 用的是无监督预训练 有监督微调&#xff0c;只有简单的单向语言模型任务&#xff1b;GPT-2用的是纯无监督预训练&#xff0c;使用更多的数据&#xff0c;更大的模型&#xff0c;又新增了几个辅助的训练任务&#xff1b;GPT-3 沿用了 GPT-2 的纯无监督预训练&a…

postgresql standby启动流程分析

专栏内容&#xff1a;postgresql内核源码分析个人主页&#xff1a;我的主页座右铭&#xff1a;天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物&#xff0e; 目录 前言 概述 原理机制 关键流程 备节点启动 walreceiver启动的时机 结尾 前…

网络协议与攻击模拟-07-TCP连接-三次握手

传输层协议 1.TCP 2.UDP TCP 协议 回顾内容 1&#xff0e;传输层功能&#xff1a;定义应用层协议数据报文的端口号&#xff0c;流量控制 2&#xff0e;对原始数据进行分段处理 传输层所提供服务 1&#xff0e;传输连接服务 2&#xff0e;数据传输服务&#xff1a;流量控制…