先说结论,升级QT到最新版本即可解决!确认官方bug!
折腾了四五天了,最后在Qt论坛找到了答案,确认有问题的版本为qt-opensource-windows-x86-5.14.1.exe:
确认已经修复问题的版本qt-opensource-windows-x86-5.12.10和qt-opensource-windows-x86-5.9.9.exe:
因为5.9.9和5.14.1关于的版本显示一致,所以通过安装文件的名字区分比较好!
===============分割线===================
使用范例官方就有,但是官方的范例并不适合反复调用,比如我每隔1秒钟就要调用一次,一旦次数过多,时间过久,则会出现明显的内存泄露,最终导致应用崩溃。
那么这个问题我是如何解决的呢?在搜索了网上许多参考资料之后,(2021.4.1更新测试结果)仍然没能彻底解决问题,只是使得内存泄露的情况缓解了许多。
那么,先来看看代码:
//author:autumoon
//联系QQ:4589968
//日期:2021-03-31
QString strCmdUrl = QString("http://") + strHost + ":" + QString::number(9998);
QNetworkRequest request(strCmdUrl);
setRequestHeader(request);
static QNetworkAccessManager* naManager = new QNetworkAccessManager;
QNetworkReply* reply = naManager->post(request, baPostData);
QEventLoop eventloop;
QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
Q_ASSERT(conRet);
QTimer timer;
timer.singleShot(nTimeoutSeconds * 1000, &eventloop, SLOT(quit()));
timer.start();
eventloop.exec(QEventLoop::ExcludeUserInputEvents);
if (timer.isActive())
{
QByteArray baReply;
if (parseReplyData(reply, baReply) == 0)
{
reply->abort();
reply->deleteLater();
//naManager->deleteLater();
baAnswer = baReply;
return 0;
}
}
else
{
//超时,未知状态
disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
}
reply->abort();
reply->deleteLater();
//naManager->deleteLater();
return -1;
注意:static QNetworkAccessManager* naManager 这个类型定义为静态变量,避免反复申请,一定要避免反复申请QNetworkAccessManager。
第二点,参考网上的资料,调用间隔最好大于100毫秒,而我的需求是间隔120ms,所以测试也没问题。
第三点,将控制权交给Application,使得事件完成,这样可以更好地释放QNetworkAccessManager和QNetworkReply等变量,前提是调用了成员函数deleteLater();
看看我的调用代码,使用了QCoreApplication::processEvents():
//注意需要避免内存泄露
ClientProgress* pCP = new ClientProgress;
int nCurProgress = pCP->CheckClientProgress(ci.strIp, ci.strProgressTxtPath, strInfo);
delete pCP;
//防止内存泄露
QCoreApplication::processEvents();
个人感觉问题应该没有从根本上解决,Qt官方应该给出一个一劳永逸的解决办法才好,但是可能存在不少困难吧,个人猜测。
===========2021.04.01补充============
经过测试,问题依然没有彻底解决,泄露的情况依然存在,我尝试了各种办法,仍然无效,只能缓解,下面给出完整的类代码和调用代码。
这里之所以封装为类,是寄希望于手动控制类的生命周期以控制内存泄露,然而无效。
#include "ClientProgress.h"
#include <QNetworkAccessManager>
#include <QEventLoop>
#include <QTimer>
#include "taskFuncList.h"
#include "JsonParser.h"
ClientProgress::ClientProgress(QObject *parent) : QObject(parent)
{
//避免反复申请
m_naManager = new QNetworkAccessManager(this);
m_naManager->setAutoDeleteReplies(true);
}
ClientProgress::~ClientProgress()
{
if(m_naManager)
{
m_naManager->deleteLater();
}
}
int ClientProgress::CheckClientProgress(std::string strHost, std::string strProgressFilePathName, std::string &strInfo)
{
QStringList lKeys, lValues;
lKeys.push_back("ip");
lKeys.push_back("fun");
lKeys.push_back("file");
lValues.push_back(strHost.c_str());
lValues.push_back(QString::number(enProgress));
lValues.push_back(strProgressFilePathName.c_str());
QByteArray baPostData = JsonParser::StringList2ByteArray(lKeys, lValues);
QByteArray baReply;
if (requestClientUrl(strHost.c_str(), baPostData, baReply, 2) == 0)
{
QString strProgress;
if (JsonParser::ParseRootKeyValue(baReply, "progress", strProgress) == 0)
{
QStringList lProgress = strProgress.split(",");
if (lProgress.size() >= 2)
{
int nProgress = lProgress[0].toInt();
if (nProgress >= 0 && nProgress <= 100)
{
strInfo = lProgress[1].toUtf8().data();
return nProgress;
}
}
}
}
return 0;
}
int ClientProgress::parseReplyData(QNetworkReply *reply, QByteArray &data)
{
if (reply == nullptr)
{
return -1;
}
if(reply->error() != QNetworkReply::NoError)
{
return -2;
}
else
{
//输出内容
data = reply->readAll();
}
return 0;
}
int ClientProgress::requestClientUrl(const QString &strHost, const QByteArray &baPostData, QByteArray &baAnswer, int nTimeoutSeconds)
{
QString strCmdUrl = QString("http://") + strHost + ":" + QString::number(9998);
QNetworkRequest request(strCmdUrl);
setRequestHeader(request);
QNetworkReply* reply = m_naManager->post(request, baPostData);
QEventLoop eventloop;
QMetaObject::Connection conRet = QObject::connect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
Q_ASSERT(conRet);
QTimer timer;
timer.singleShot(nTimeoutSeconds * 1000, &eventloop, SLOT(quit()));
timer.start();
eventloop.exec(QEventLoop::ExcludeUserInputEvents);
if (timer.isActive())
{
QByteArray baReply;
if (parseReplyData(reply, baReply) == 0)
{
reply->abort();
reply->close();
reply->deleteLater();
//naManager->deleteLater();
baAnswer = baReply;
return 0;
}
}
else
{
//超时,未知状态
disconnect(reply, SIGNAL(finished()), &eventloop, SLOT(quit()));
}
reply->abort();
reply->close();
reply->deleteLater();
//naManager->deleteLater();
return -1;
}
没有使用对象数,因为是主线程,调用代码:
//注意需要避免内存泄露
ClientProgress* pCP = new ClientProgress;
int nCurProgress = pCP->CheckClientProgress(ci.strIp, ci.strProgressTxtPath, strInfo);
//pCP->deleteLater(); //此时new的时候加上this
delete pCP;
pCP = nullptr;
//防止内存泄露
QCoreApplication::processEvents();
此方法依然无法彻底解决问题,欢迎交流与讨论。