当前位置: 首页 > 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中间件是典型的消息队列,技术上是典型的生产者<-->消费者的订阅/分发模型,可以减轻很大开发工作。

http://www.lryc.cn/news/423398.html

相关文章:

  • 数组列表中的最大距离
  • C语言新手小白详细教程(7)指针和指针变量
  • Kafka保证消息不丢失
  • 数据结构+基数排序算法
  • C++ list【常用接口、模拟实现等】
  • 12.面试题——Spring Boot
  • 【前端VUE】npm i 出现版本错误等报错 简单直接解决命令
  • 精彩回顾 | 风丘科技亮相2024名古屋汽车工程博览会
  • 设计模式21-组合模式
  • 如何选择深度学习的损失函数和激活函数
  • DATAX自定义KafkaWriter
  • Mybatis分页多表多条件查询
  • SpringBoot快速入门(手动创建)
  • C 408—《数据结构》算法题基础篇—数组(通俗易懂)
  • AI秘境-墨小黑奇遇记 - 初体验(一)
  • 文件IO813
  • STP(生成树)的概述和工作原理
  • 从AGV到立库,物流自动化的更迭与未来
  • 阴阳脚数码管
  • 【Vue3-Typescript】<script setup lang=“ts“> 使用 ref标签 怎么获取 refs子组件呢
  • npm 超详细使用教程
  • TypeScript函数
  • 中海油某海上平台轨道巡检机器人解决方案
  • 【NXP-MCXA153】SPI驱动移植
  • Python if 编程题|Python一对一辅导教学
  • 机器学习——第十一章 特征选择与稀疏学习
  • 花式表演无人机技术详解
  • 服务器那点事--防火墙
  • C:每日一题:单身狗
  • SQL之使用存储过程循环插入数据