当前位置: 首页 > news >正文

【QT常用技术讲解】多线程处理+全局变量处理异步事件并获取多个线程返回的结果

前言

       QTableView加入勾选项后(参考【QT常用技术讲解】QTableView添加QCheckBox、QPushButton),如果支持右键菜单功能,此时就有统一执行多个异步事件,并且统一输出到界面的需求了,本篇结合多线程+共享全局变量进行开发。

概述

        QT开发中,单个异步事件处理(比如网络通信ping一个IP,或者是执行一条后台命令后等待返回结果),创建一个线程,并通过connect关联信号和槽,异步获取到一条结果即可,如果是多个同种类型异步事件需要同时处理(比如网络通信ping多个IP,或者是执行多条后台命令后等待返回结果),此时就需要创建多个线程,并且用共享全局变量收集全部结果进行统一处理。

        本篇通过同时开多个线程ping IP,并获取全部结果后,通过对话框展示结果来实现整体功能。

功能详解

1、增加共享全局变量

增加了两个文件:globalvalue.h和globalvalue.cpp,对变量进行声明和定义,代码如下所示:

//globalvalue.h源代码
#ifndef GLOBALVALUE_H
#define GLOBALVALUE_H
#include <QString>
#include <QList>namespace GlobalVariables {//声明全局变量extern QList<QString> g_calbackmsg;
}#endif // GLOBALVALUE_H
//globalvalue.cpp源代码
#include "globalvalue.h"namespace GlobalVariables {//定义全局变量QList<QString> g_calbackmsg;
}

2、改造命令处理模块

原来是单线程时,后台命令处理线程完成任务之后,通过发送信号 emit callback()把结果输出给PING模块即可,而多条命令时,则通过互斥锁+全局共享变量获取返回结果来处理。

    //多线程调用共享变量,需要上互斥锁mutex.lock();//qDebug() << __LINE__ << "add ret:"<<retstr;g_calbackmsg.append(retstr);mutex.unlock();

完整代码如下:

//threadCmd.cpp源代码
#ifndef THREADCMD_CPP
#define THREADCMD_CPP#include "threadCmd.h"
#include <QDebug>
#include <QProcess>
#include "src/util/comm_define.h"
#include <QTextCodec>
//增加全局变量头文件和命名空间,以及互斥锁
#include <QMutex>
#include "src/util/globalvalue.h"
using namespace GlobalVariables;
QMutex mutex;ThreadCmd::ThreadCmd(const QString &param,QObject *parent) : QThread(parent),m_param(param) {}void ThreadCmd::run(){QString retstr="";QProcess process;// 执行命令QString cmd = m_param;//qDebug() << __LINE__ << cmd;process.start(cmd);if(process.waitForFinished()){// 读取进程的输出QString output = QString::fromLocal8Bit(process.readAllStandardOutput());QTextCodec *codec = QTextCodec::codecForName("GBK");QString unicodeOutput = codec->toUnicode(output.toLocal8Bit());//emit callback(unicodeOutput);retstr=QString("[error]:命令[%1]命令执行成功").arg(cmd);}else{// emit callback("[error]:命令执行失败");retstr=QString("[error]:命令[%1]命令执行错误").arg(cmd);}//多线程调用共享变量,需要上互斥锁mutex.lock();//qDebug() << __LINE__ << "add ret:"<<retstr;g_calbackmsg.append(retstr);mutex.unlock();//emit callback(retstr);----多线程用了共享全局变量之后,不需要返回此值// 进程使用完毕后,可以手动删除process.deleteLater();exit(0);
}#endif // THREADCMD_CPP

3、ping模块多线程改造

对按钮事件进行改造,创建多线程之前,通过g_calbackmsg.clear()先请客全局共享变量,创建线程后,增加了多线程列表threadList来启动和删除线程,另外单独增加一个线程来处理结果ThreadDisplaymsg,代码如下所示:

//pingdialog.cpp源代码
void pingDialog::on_start_clicked()
{bool bfind=false;ui->textBrowser->setReadOnly(false);//ui->textBrowser->setText("--------ping start--------");QString times = ui->timeBox->currentText();QString Ipstr=ui->ipEdit->text();//处理多个IP,组装后台执行命令,并调用线程取执行g_calbackmsg.clear();QStringList iplist = Ipstr.split(";");for(int  i=0;i<iplist.size();i++){QString ip=iplist[i];QString cmd = QString("ping -n %1 %2").arg(times).arg(ip);qDebug() << __LINE__ <<"cmd:" <<cmd;//把调用QProcess执行后台命令的代码改成调用多线程类ThreadCmd *thread = new ThreadCmd(cmd, this);//等待多线程的callback信号,关联ThreadsendResult槽函数来处理结果connect(thread, &ThreadCmd::callback, this, &pingDialog::ThreadsendResult);threadList.append(thread);thread->start();bfind = true;}qDebug()<< __LINE__ << __FUNCTION__ << bfind;if(bfind){//创建专门的结果处理线程来弹窗对话框显示结果ThreadDisplaymsg *dthread = new ThreadDisplaymsg(iplist,this);connect(dthread, &ThreadDisplaymsg::callback, this, &pingDialog::DisplayResult);dthread->start();}this->close();}

4、新增结果处理线程ThreadDisplaymsg

增加了两个文件:threadDisplaymsg.h和threadDisplaymsg.cpp,

//threadDisplaymsg.h源代码
#ifndef THREADDISPLAYMSG_H
#define THREADDISPLAYMSG_H#include <QThread>
#include <QString>
#include "src/util/globalvalue.h"  //引用全局变量的头文件
using namespace GlobalVariables;   //声明全局变量的命名空间class ThreadDisplaymsg : public QThread
{Q_OBJECT
public:explicit ThreadDisplaymsg(const QStringList &param,QObject *parent = nullptr) ;protected:void run() override;
signals:void callback(const QString result);
private:QStringList m_paramlist;
};#endif // THREADDISPLAYMSG_H
//threadDisplaymsg.cpp源代码
#ifndef THREADDISPLAYMSG_CPP
#define THREADDISPLAYMSG_CPP#include "threadDisplaymsg.h"
#include <QDebug>
#include <QMessageBox>//传递参数param进来
ThreadDisplaymsg::ThreadDisplaymsg(const QStringList &param,QObject *parent) : QThread(parent),m_paramlist(param) {}void ThreadDisplaymsg::run(){int len = m_paramlist.size();//m_paramlist是IP列表,即IP数量,也等于开启线程的数量//qDebug() << "ThreadDisplaymsg:" <<__LINE__ << len;int loop_num=0;int max_num=100;//如果超过100秒还没获取到结果,说明你要优化的是异步的响应机制,而不是当前的代码,100秒都给不了结果,体验得有多差while(loop_num<max_num){//等待全部的线程返回结果if(g_calbackmsg.size()==len)//结果数量break;loop_num++;QThread::sleep(1);}qDebug() << "ThreadDisplaymsg wait for " <<loop_num << "s";QString result="";for(QString msg : g_calbackmsg) {result += msg;result += "\n";}qDebug() << "=====ThreadDisplaymsg callback=======";//所有的结果累加完成后发送信号callbackemit callback(result);
}#endif // THREADDISPLAYMSG_CPP

5、QTableView表的模块改动

        改动位置是右键菜单功能的信号和槽进行优化,增加了disconnect()来断掉connect()的链接,如下代码,如果注释掉disconnect()功能,每次点击ping功能菜单之后,disconnect()就会累加1,导致对话框弹出N个,不能等系统自动回收,需要disconnect()关掉,并且是在connect()之前关掉,因为多线程是异步的:先connect后disconnect的话,多线程还没返回结果,已经执行disconnect了,这时候connect就被关闭了。

void tab_basemsg::tableWidget_MenuRequested(const QPoint &pos) {QModelIndex index = ui->tableWidget->indexAt(pos);//获取当前行if (!index.isValid()) return;QMenu menu(this);//单选项才有查看详情if(m_iplist.size()<=1){QAction *DiplaymsgAction = menu.addAction(tr("查看详情"));connect(DiplaymsgAction,&QAction::triggered,[=](){//获取选择的单元格QList<QTableWidgetItem *> selected_cells = ui->tableWidget->selectedItems();if(!selected_cells.isEmpty()){QTableWidgetItem *codeCell = ui->tableWidget->item(selected_cells.first()->row(),HEAD_BASEMSG_CODE);if(codeCell!=nullptr){QString code = codeCell->text();stBasemsg basemsg=m_basemsgmap[code];basemsgDialg->setModal(false);basemsgDialg->setWindowTitle("保存");//basemsgDialg->setFixedSize(500,400);basemsgDialg->open();basemsgDialg->init(1,basemsg);basemsgDialg->exec();}}});}QAction *NetpingAction = menu.addAction(tr("Ping此计算机"));connect(NetpingAction,&QAction::triggered,[=](){QString ipstr;for(int  i=0;i<m_iplist.size();i++){QString ip=m_iplist[i];ipstr += ip;if(i!=(m_iplist.size()-1)){ipstr += ";";}}qDebug() << __LINE__ << __FUNCTION__ << ipstr;pingdlg->setIp(ipstr);pingdlg->setModal(false);pingdlg->setWindowTitle("PING测试");pingdlg->setFixedSize(500,400);pingdlg->open();pingdlg->init();pingdlg->exec();// 先断开之前的连接--如果不断开链接,每次操作此功能时,connect都+1,就会出现多次弹窗disconnect(pingdlg, &pingDialog::sendtobasemsg, this, nullptr);//connect要放在disconnect后面:即先关闭上一次的connect,再出现创建connect//因为多线程是异步的:先connect后disconnect的话,多线程还没返回结果,已经执行disconnect了,这时候connect就被关闭了connect(pingdlg,&pingDialog::sendtobasemsg,this,&tab_basemsg::displaysendFileresult);});menu.exec(QCursor::pos());//menu.exec(ui->tableWidget->mapToGlobal(pos));
}

篇尾

        全局变量是多线程中比较轻量级的共享机制,有很多成熟的消息传递技术,如果你的功能带有业务性,建议多调研选型,比如MQ中间件是典型的消息队列,技术上是典型的生产者<-->消费者的订阅/分发模型,可以减轻很大开发工作。

相关文章:

【QT常用技术讲解】多线程处理+全局变量处理异步事件并获取多个线程返回的结果

前言 QTableView加入勾选项后&#xff08;参考【QT常用技术讲解】QTableView添加QCheckBox、QPushButton&#xff09;&#xff0c;如果支持右键菜单功能&#xff0c;此时就有统一执行多个异步事件&#xff0c;并且统一输出到界面的需求了&#xff0c;本篇结合多线程共享全局变量…...

数组列表中的最大距离

给定 m 个数组&#xff0c;每个数组都已经按照升序排好序了。现在你需要从两个不同的数组中选择两个整数&#xff08;每个数组选一个&#xff09;并且计算它们的距离。两个整数 a 和 b 之间的距离定义为它们差的绝对值 |a-b| 。你的任务就是去找到最大距离 示例 1&#xff1a;…...

C语言新手小白详细教程(7)指针和指针变量

希望文章能够给到初学的你一些启发&#xff5e; 如果觉得文章对你有帮助的话&#xff0c;点赞 关注 收藏支持一下笔者吧&#xff5e; 阅读指南&#xff1a; 开篇说明1、指针的定义接下来我们用图示的形式来解释一下 指针&#xff1a;2、申明指针变量3、取地址符 &4、为指针…...

Kafka保证消息不丢失

Kafka保证消息不丢失 生产者发送消息到Broker丢失 设置异步发送 回调方法中的参数Exception e如果为空 代表发送成功,如果不为空代表发送失败出现异常 消息在Broker中丢失 kafka集群中存在分区机制 分区中分为leader和follower副本 leader负责读写,而follower只负责数据…...

数据结构+基数排序算法

一、问题描述 实现英文单词按字典序排列的基数排序算法 编写一个程序&#xff0c;采用基数排序方法将一组英文单词按字典顺序排 列。假设单词均由小写字母或空格构成&#xff0c;最长的单词有 MaxLen 个 字母&#xff0c;用相关数据进行测试并输出各趟的排序结果。 用例&#…...

C++ list【常用接口、模拟实现等】

1. list的介绍及使用 1.1 list的介绍 1.list是可以在常数范围内在任意位置进行插入和删除的序列式容器&#xff0c;并且该容器可以前后双向迭代。 2.list的底层是双向链表结构&#xff0c;双向链表中每个元素存储在互不相关的独立节点中&#xff0c;在节点中通过指针指向其前…...

12.面试题——Spring Boot

1.Spring Boot是什么&#xff1f; Spring Boot 是 Spring 开源组织下的子项目&#xff0c;是 Spring 组件一站式解决方案&#xff0c;主要是简化了使用 Spring 的难度&#xff0c;简省了繁重的配置&#xff0c;提供了各种启动器&#xff0c;开发者能快速上手。 2.为什么要用 …...

【前端VUE】npm i 出现版本错误等报错 简单直接解决命令

前端vue npm i 安装时出现 报错原因 在新版本的npm中&#xff0c;默认情况下&#xff0c;npm install遇到冲突的peerDependencies时将失败。 解决办法 使用--force或--legacy-peer-deps可解决这种情况。 --force 会无视冲突&#xff0c;并强制获取远端npm库资源&#xff0…...

精彩回顾 | 风丘科技亮相2024名古屋汽车工程博览会

2024年7月17日-19日&#xff0c;风丘科技联合德国IPETRONIK亮相日本名古屋汽车工程博览会。该展会面向汽车行业不同应用场景&#xff0c;包括新的eAxle、FCEV、ADAS、测试测量系统和ECU测试等相关技术&#xff0c;是一个专为活跃在汽车行业前线的工程师和研究人员举办的汽车技术…...

设计模式21-组合模式

设计模式21-组合模式&#xff08;Composite Pattern&#xff09; 写在前面 动机定义与结构定义结构主要类及其关系 C代码推导优缺点应用场景总结补充叶子节点不重载这三个方法叶子节点重载这三个方法结论 写在前面 数据结构模式 常常有一些组件在内部具有特定的数据结构。如何…...

如何选择深度学习的损失函数和激活函数

一概述 在深度学习中&#xff0c;损失函数&#xff08;Loss Function&#xff09;和激活函数&#xff08;Activation Function&#xff09;是两个至关重要的组件&#xff0c;它们共同影响着模型的训练效果和泛化能力。本文将简要介绍这两个概念&#xff0c;阐述选择它们的重要性…...

DATAX自定义KafkaWriter

因为datax目前不支持写入数据到kafka中&#xff0c;因此本文主要介绍如何基于DataX自定义KafkaWriter&#xff0c;用来同步数据到kafka中。本文偏向实战&#xff0c;datax插件开发理论宝典请参考官方文档&#xff1a; https://github.com/alibaba/DataX/blob/master/dataxPlug…...

Mybatis分页多表多条件查询

个人总结三种方式&#xff1a; Xml、queryWrapper、PageHelper第三方组件这三种方式进行查询&#xff1b; 方式一&#xff1a; xml中联表查询&#xff0c;在mapper中传参IPage<T>和条件Map&#xff08;这里用map装参数&#xff09;。 代码示例&#xff1a; Mapper层 M…...

SpringBoot快速入门(手动创建)

目录 案例&#xff1a;需求 步骤 1 创建Maven项目 2 导入SpringBoot起步依赖 3 定义Controller 4 编写引导类 案例&#xff1a;需求 搭建简单的SpringBoot工程&#xff0c;创建hello的类定义h1的方法&#xff0c;返回Hello SpringBoot! 步骤 1 创建Maven项目 大家&…...

C 408—《数据结构》算法题基础篇—数组(通俗易懂)

目录 Δ前言 一、数组的合并 0.题目&#xff1a; 1.算法设计思想&#xff1a; 2.C语言描述&#xff1a; 3.算法的时间和空间复杂度 : 二、数组元素的倒置 0.题目 : 1.算法设计思想 : 2.C语言描述 : 3.算法的时间和空间复杂度 : 三、数组中特定值元素的删除 0.题目 : …...

AI秘境-墨小黑奇遇记 - 初体验(一)

“怎么可能&#xff01;”墨小黑盯着屏幕上的代码&#xff0c;整个人都不好了。调试了三遍&#xff0c;翻了几遍书&#xff0c;结果还是不对。就像你以为自己早起赶车&#xff0c;结果发现闹钟根本没响一样崩溃。 这是他第一次真正接触人工智能实战任务——实现一个简单的感知…...

文件IO813

标准IO文件定位&#xff1a; fseek函数&#xff1a; 功能&#xff1a;将stream流文件中的文件指针从whence位置开始偏移offset个字节的长度。 int fseek(FILE *stream , long offset, int whence); FILE *stream 指的是所需要定位的文件&#xff08;文化定位前提是文件要被打…...

STP(生成树)的概述和工作原理

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:Linux运维老纪的首页…...

从AGV到立库,物流自动化的更迭与未来

AGV叉车 随着柔性制造系统的广泛应用&#xff0c;小批量、多批次的生产需求不断增强&#xff0c;“订单导向”生产已经成为趋势。这也让越来越多的企业认识到&#xff0c;产线的智能设备导入只是第一步&#xff0c;要想达到生产效率的最优解&#xff0c;物流系统的再优化必须提…...

阴阳脚数码管

1.小故事 最近&#xff0c;我接到了一个既“清肺”又“烧脑”的新任务&#xff0c;设计一个低功耗蓝牙肺活量计。在这个项目中我们借鉴了一款蓝牙跳绳的硬件设计方案&#xff0c;特别是它的显示方案——数码管。 在电子工程领域&#xff0c;初学者往往从操作LED开始&#xff…...

web vue 项目 Docker化部署

Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段&#xff1a; 构建阶段&#xff08;Build Stage&#xff09;&#xff1a…...

idea大量爆红问题解决

问题描述 在学习和工作中&#xff0c;idea是程序员不可缺少的一个工具&#xff0c;但是突然在有些时候就会出现大量爆红的问题&#xff0c;发现无法跳转&#xff0c;无论是关机重启或者是替换root都无法解决 就是如上所展示的问题&#xff0c;但是程序依然可以启动。 问题解决…...

Ubuntu系统下交叉编译openssl

一、参考资料 OpenSSL&&libcurl库的交叉编译 - hesetone - 博客园 二、准备工作 1. 编译环境 宿主机&#xff1a;Ubuntu 20.04.6 LTSHost&#xff1a;ARM32位交叉编译器&#xff1a;arm-linux-gnueabihf-gcc-11.1.0 2. 设置交叉编译工具链 在交叉编译之前&#x…...

vulnyx Blogger writeup

信息收集 arp-scan nmap 获取userFlag 上web看看 一个默认的页面&#xff0c;gobuster扫一下目录 可以看到扫出的目录中得到了一个有价值的目录/wordpress&#xff0c;说明目标所使用的cms是wordpress&#xff0c;访问http://192.168.43.213/wordpress/然后查看源码能看到 这…...

GitHub 趋势日报 (2025年06月06日)

&#x1f4ca; 由 TrendForge 系统生成 | &#x1f310; https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日获星趋势图 今日获星趋势图 590 cognee 551 onlook 399 project-based-learning 348 build-your-own-x 320 ne…...

ubuntu系统文件误删(/lib/x86_64-linux-gnu/libc.so.6)修复方案 [成功解决]

报错信息&#xff1a;libc.so.6: cannot open shared object file: No such file or directory&#xff1a; #ls, ln, sudo...命令都不能用 error while loading shared libraries: libc.so.6: cannot open shared object file: No such file or directory重启后报错信息&…...

macOS 终端智能代理检测

&#x1f9e0; 终端智能代理检测&#xff1a;自动判断是否需要设置代理访问 GitHub 在开发中&#xff0c;使用 GitHub 是非常常见的需求。但有时候我们会发现某些命令失败、插件无法更新&#xff0c;例如&#xff1a; fatal: unable to access https://github.com/ohmyzsh/oh…...

C++--string的模拟实现

一,引言 string的模拟实现是只对string对象中给的主要功能经行模拟实现&#xff0c;其目的是加强对string的底层了解&#xff0c;以便于在以后的学习或者工作中更加熟练的使用string。本文中的代码仅供参考并不唯一。 二,默认成员函数 string主要有三个成员变量&#xff0c;…...

Java多线程实现之Runnable接口深度解析

Java多线程实现之Runnable接口深度解析 一、Runnable接口概述1.1 接口定义1.2 与Thread类的关系1.3 使用Runnable接口的优势 二、Runnable接口的基本实现方式2.1 传统方式实现Runnable接口2.2 使用匿名内部类实现Runnable接口2.3 使用Lambda表达式实现Runnable接口 三、Runnabl…...

比特币:固若金汤的数字堡垒与它的四道防线

第一道防线&#xff1a;机密信函——无法破解的哈希加密 将每一笔比特币交易比作一封在堡垒内部传递的机密信函。 解释“哈希”&#xff08;Hashing&#xff09;就是一种军事级的加密术&#xff08;SHA-256&#xff09;&#xff0c;能将信函内容&#xff08;交易细节&#xf…...