赞
踩
QTableView加入勾选项后(参考【QT常用技术讲解】QTableView添加QCheckBox、QPushButton),如果支持右键菜单功能,此时就有统一执行多个异步事件,并且统一输出到界面的需求了,本篇结合多线程+共享全局变量进行开发。
QT开发中,单个异步事件处理(比如网络通信ping一个IP,或者是执行一条后台命令后等待返回结果),创建一个线程,并通过connect关联信号和槽,异步获取到一条结果即可,如果是多个同种类型异步事件需要同时处理(比如网络通信ping多个IP,或者是执行多条后台命令后等待返回结果),此时就需要创建多个线程,并且用共享全局变量收集全部结果进行统一处理。
本篇通过同时开多个线程ping IP,并获取全部结果后,通过对话框展示结果来实现整体功能。
增加了两个文件: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;
- }
原来是单线程时,后台命令处理线程完成任务之后,通过发送信号 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 ¶m,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
对按钮事件进行改造,创建多线程之前,通过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();
-
- }
增加了两个文件: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 ¶m,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 ¶m,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=======";
- //所有的结果累加完成后发送信号callback
- emit callback(result);
- }
-
- #endif // THREADDISPLAYMSG_CPP
改动位置是右键菜单功能的信号和槽进行优化,增加了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中间件是典型的消息队列,技术上是典型的生产者<-->消费者的订阅/分发模型,可以减轻很大开发工作。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。