北京便宜網(wǎng)站建設(shè)德國搜索引擎
前言
? ? ? ?QTableView加入勾選項(xiàng)后(參考【QT常用技術(shù)講解】QTableView添加QCheckBox、QPushButton),如果支持右鍵菜單功能,此時(shí)就有統(tǒng)一執(zhí)行多個(gè)異步事件,并且統(tǒng)一輸出到界面的需求了,本篇結(jié)合多線程+共享全局變量進(jìn)行開發(fā)。
概述
????????QT開發(fā)中,單個(gè)異步事件處理(比如網(wǎng)絡(luò)通信ping一個(gè)IP,或者是執(zhí)行一條后臺(tái)命令后等待返回結(jié)果),創(chuàng)建一個(gè)線程,并通過connect關(guān)聯(lián)信號(hào)和槽,異步獲取到一條結(jié)果即可,如果是多個(gè)同種類型異步事件需要同時(shí)處理(比如網(wǎng)絡(luò)通信ping多個(gè)IP,或者是執(zhí)行多條后臺(tái)命令后等待返回結(jié)果),此時(shí)就需要?jiǎng)?chuàng)建多個(gè)線程,并且用共享全局變量收集全部結(jié)果進(jìn)行統(tǒng)一處理。
? ? ? ? 本篇通過同時(shí)開多個(gè)線程ping IP,并獲取全部結(jié)果后,通過對(duì)話框展示結(jié)果來實(shí)現(xiàn)整體功能。
功能詳解
1、增加共享全局變量
增加了兩個(gè)文件:globalvalue.h和globalvalue.cpp,對(duì)變量進(jìn)行聲明和定義,代碼如下所示:
//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、改造命令處理模塊
原來是單線程時(shí),后臺(tái)命令處理線程完成任務(wù)之后,通過發(fā)送信號(hào)?emit callback()把結(jié)果輸出給PING模塊即可,而多條命令時(shí),則通過互斥鎖+全局共享變量獲取返回結(jié)果來處理。
//多線程調(diào)用共享變量,需要上互斥鎖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;// 執(zhí)行命令QString cmd = m_param;//qDebug() << __LINE__ << cmd;process.start(cmd);if(process.waitForFinished()){// 讀取進(jìn)程的輸出QString output = QString::fromLocal8Bit(process.readAllStandardOutput());QTextCodec *codec = QTextCodec::codecForName("GBK");QString unicodeOutput = codec->toUnicode(output.toLocal8Bit());//emit callback(unicodeOutput);retstr=QString("[error]:命令[%1]命令執(zhí)行成功").arg(cmd);}else{// emit callback("[error]:命令執(zhí)行失敗");retstr=QString("[error]:命令[%1]命令執(zhí)行錯(cuò)誤").arg(cmd);}//多線程調(diào)用共享變量,需要上互斥鎖mutex.lock();//qDebug() << __LINE__ << "add ret:"<<retstr;g_calbackmsg.append(retstr);mutex.unlock();//emit callback(retstr);----多線程用了共享全局變量之后,不需要返回此值// 進(jìn)程使用完畢后,可以手動(dòng)刪除process.deleteLater();exit(0);
}#endif // THREADCMD_CPP
3、ping模塊多線程改造
對(duì)按鈕事件進(jìn)行改造,創(chuàng)建多線程之前,通過g_calbackmsg.clear()先請(qǐng)客全局共享變量,創(chuàng)建線程后,增加了多線程列表threadList來啟動(dòng)和刪除線程,另外單獨(dú)增加一個(gè)線程來處理結(jié)果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();//處理多個(gè)IP,組裝后臺(tái)執(zhí)行命令,并調(diào)用線程取執(zhí)行g(shù)_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;//把調(diào)用QProcess執(zhí)行后臺(tái)命令的代碼改成調(diào)用多線程類ThreadCmd *thread = new ThreadCmd(cmd, this);//等待多線程的callback信號(hào),關(guān)聯(lián)ThreadsendResult槽函數(shù)來處理結(jié)果connect(thread, &ThreadCmd::callback, this, &pingDialog::ThreadsendResult);threadList.append(thread);thread->start();bfind = true;}qDebug()<< __LINE__ << __FUNCTION__ << bfind;if(bfind){//創(chuàng)建專門的結(jié)果處理線程來彈窗對(duì)話框顯示結(jié)果ThreadDisplaymsg *dthread = new ThreadDisplaymsg(iplist,this);connect(dthread, &ThreadDisplaymsg::callback, this, &pingDialog::DisplayResult);dthread->start();}this->close();}
4、新增結(jié)果處理線程ThreadDisplaymsg
增加了兩個(gè)文件: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>//傳遞參數(shù)param進(jìn)來
ThreadDisplaymsg::ThreadDisplaymsg(const QStringList ¶m,QObject *parent) : QThread(parent),m_paramlist(param) {}void ThreadDisplaymsg::run(){int len = m_paramlist.size();//m_paramlist是IP列表,即IP數(shù)量,也等于開啟線程的數(shù)量//qDebug() << "ThreadDisplaymsg:" <<__LINE__ << len;int loop_num=0;int max_num=100;//如果超過100秒還沒獲取到結(jié)果,說明你要優(yōu)化的是異步的響應(yīng)機(jī)制,而不是當(dāng)前的代碼,100秒都給不了結(jié)果,體驗(yàn)得有多差while(loop_num<max_num){//等待全部的線程返回結(jié)果if(g_calbackmsg.size()==len)//結(jié)果數(shù)量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=======";//所有的結(jié)果累加完成后發(fā)送信號(hào)callbackemit callback(result);
}#endif // THREADDISPLAYMSG_CPP
5、QTableView表的模塊改動(dòng)
????????改動(dòng)位置是右鍵菜單功能的信號(hào)和槽進(jìn)行優(yōu)化,增加了disconnect()來斷掉connect()的鏈接,如下代碼,如果注釋掉disconnect()功能,每次點(diǎn)擊ping功能菜單之后,disconnect()就會(huì)累加1,導(dǎo)致對(duì)話框彈出N個(gè),不能等系統(tǒng)自動(dòng)回收,需要disconnect()關(guān)掉,并且是在connect()之前關(guān)掉,因?yàn)槎嗑€程是異步的:先connect后disconnect的話,多線程還沒返回結(jié)果,已經(jīng)執(zhí)行disconnect了,這時(shí)候connect就被關(guān)閉了。
void tab_basemsg::tableWidget_MenuRequested(const QPoint &pos) {QModelIndex index = ui->tableWidget->indexAt(pos);//獲取當(dāng)前行if (!index.isValid()) return;QMenu menu(this);//單選項(xiàng)才有查看詳情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此計(jì)算機(jī)"));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測(cè)試");pingdlg->setFixedSize(500,400);pingdlg->open();pingdlg->init();pingdlg->exec();// 先斷開之前的連接--如果不斷開鏈接,每次操作此功能時(shí),connect都+1,就會(huì)出現(xiàn)多次彈窗disconnect(pingdlg, &pingDialog::sendtobasemsg, this, nullptr);//connect要放在disconnect后面:即先關(guān)閉上一次的connect,再出現(xiàn)創(chuàng)建connect//因?yàn)槎嗑€程是異步的:先connect后disconnect的話,多線程還沒返回結(jié)果,已經(jīng)執(zhí)行disconnect了,這時(shí)候connect就被關(guān)閉了connect(pingdlg,&pingDialog::sendtobasemsg,this,&tab_basemsg::displaysendFileresult);});menu.exec(QCursor::pos());//menu.exec(ui->tableWidget->mapToGlobal(pos));
}
篇尾
? ? ? ? 全局變量是多線程中比較輕量級(jí)的共享機(jī)制,有很多成熟的消息傳遞技術(shù),如果你的功能帶有業(yè)務(wù)性,建議多調(diào)研選型,比如MQ中間件是典型的消息隊(duì)列,技術(shù)上是典型的生產(chǎn)者<-->消費(fèi)者的訂閱/分發(fā)模型,可以減輕很大開發(fā)工作。