当前位置:   article > 正文

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

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

前言

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

概述

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

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

功能详解

1、增加共享全局变量

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

  1. //globalvalue.h源代码
  2. #ifndef GLOBALVALUE_H
  3. #define GLOBALVALUE_H
  4. #include <QString>
  5. #include <QList>
  6. namespace GlobalVariables {//声明全局变量
  7. extern QList<QString> g_calbackmsg;
  8. }
  9. #endif // GLOBALVALUE_H
  1. //globalvalue.cpp源代码
  2. #include "globalvalue.h"
  3. namespace GlobalVariables {//定义全局变量
  4. QList<QString> g_calbackmsg;
  5. }

2、改造命令处理模块

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

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

完整代码如下:

  1. //threadCmd.cpp源代码
  2. #ifndef THREADCMD_CPP
  3. #define THREADCMD_CPP
  4. #include "threadCmd.h"
  5. #include <QDebug>
  6. #include <QProcess>
  7. #include "src/util/comm_define.h"
  8. #include <QTextCodec>
  9. //增加全局变量头文件和命名空间,以及互斥锁
  10. #include <QMutex>
  11. #include "src/util/globalvalue.h"
  12. using namespace GlobalVariables;
  13. QMutex mutex;
  14. ThreadCmd::ThreadCmd(const QString &param,QObject *parent) : QThread(parent),m_param(param) {
  15. }
  16. void ThreadCmd::run(){
  17. QString retstr="";
  18. QProcess process;
  19. // 执行命令
  20. QString cmd = m_param;
  21. //qDebug() << __LINE__ << cmd;
  22. process.start(cmd);
  23. if(process.waitForFinished()){
  24. // 读取进程的输出
  25. QString output = QString::fromLocal8Bit(process.readAllStandardOutput());
  26. QTextCodec *codec = QTextCodec::codecForName("GBK");
  27. QString unicodeOutput = codec->toUnicode(output.toLocal8Bit());
  28. //emit callback(unicodeOutput);
  29. retstr=QString("[error]:命令[%1]命令执行成功").arg(cmd);
  30. }else{
  31. // emit callback("[error]:命令执行失败");
  32. retstr=QString("[error]:命令[%1]命令执行错误").arg(cmd);
  33. }
  34. //多线程调用共享变量,需要上互斥锁
  35. mutex.lock();
  36. //qDebug() << __LINE__ << "add ret:"<<retstr;
  37. g_calbackmsg.append(retstr);
  38. mutex.unlock();
  39. //emit callback(retstr);----多线程用了共享全局变量之后,不需要返回此值
  40. // 进程使用完毕后,可以手动删除
  41. process.deleteLater();
  42. exit(0);
  43. }
  44. #endif // THREADCMD_CPP

3、ping模块多线程改造

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

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

4、新增结果处理线程ThreadDisplaymsg

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

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

5、QTableView表的模块改动

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

  1. void tab_basemsg::tableWidget_MenuRequested(const QPoint &pos) {
  2. QModelIndex index = ui->tableWidget->indexAt(pos);//获取当前行
  3. if (!index.isValid()) return;
  4. QMenu menu(this);
  5. //单选项才有查看详情
  6. if(m_iplist.size()<=1){
  7. QAction *DiplaymsgAction = menu.addAction(tr("查看详情"));
  8. connect(DiplaymsgAction,&QAction::triggered,[=](){
  9. //获取选择的单元格
  10. QList<QTableWidgetItem *> selected_cells = ui->tableWidget->selectedItems();
  11. if(!selected_cells.isEmpty()){
  12. QTableWidgetItem *codeCell = ui->tableWidget->item(selected_cells.first()->row(),HEAD_BASEMSG_CODE);
  13. if(codeCell!=nullptr){
  14. QString code = codeCell->text();
  15. stBasemsg basemsg=m_basemsgmap[code];
  16. basemsgDialg->setModal(false);
  17. basemsgDialg->setWindowTitle("保存");
  18. //basemsgDialg->setFixedSize(500,400);
  19. basemsgDialg->open();
  20. basemsgDialg->init(1,basemsg);
  21. basemsgDialg->exec();
  22. }
  23. }
  24. });
  25. }
  26. QAction *NetpingAction = menu.addAction(tr("Ping此计算机"));
  27. connect(NetpingAction,&QAction::triggered,[=](){
  28. QString ipstr;
  29. for(int i=0;i<m_iplist.size();i++){
  30. QString ip=m_iplist[i];
  31. ipstr += ip;
  32. if(i!=(m_iplist.size()-1)){
  33. ipstr += ";";
  34. }
  35. }
  36. qDebug() << __LINE__ << __FUNCTION__ << ipstr;
  37. pingdlg->setIp(ipstr);
  38. pingdlg->setModal(false);
  39. pingdlg->setWindowTitle("PING测试");
  40. pingdlg->setFixedSize(500,400);
  41. pingdlg->open();
  42. pingdlg->init();
  43. pingdlg->exec();
  44. // 先断开之前的连接--如果不断开链接,每次操作此功能时,connect都+1,就会出现多次弹窗
  45. disconnect(pingdlg, &pingDialog::sendtobasemsg, this, nullptr);
  46. //connect要放在disconnect后面:即先关闭上一次的connect,再出现创建connect
  47. //因为多线程是异步的:先connect后disconnect的话,多线程还没返回结果,已经执行disconnect了,这时候connect就被关闭了
  48. connect(pingdlg,&pingDialog::sendtobasemsg,this,&tab_basemsg::displaysendFileresult);
  49. });
  50. menu.exec(QCursor::pos());
  51. //menu.exec(ui->tableWidget->mapToGlobal(pos));
  52. }

篇尾

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

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/码创造者/article/detail/1005464
推荐阅读
相关标签
  

闽ICP备14008679号