发布请求和 QNetworkAccessManager 的内存泄漏

Posted

技术标签:

【中文标题】发布请求和 QNetworkAccessManager 的内存泄漏【英文标题】:Memory leak with post requests and QNetworkAccessManager 【发布时间】:2013-12-02 13:36:49 【问题描述】:

我正在制作一个使用大量计时器的程序,并以 4 秒的间隔向php 脚本发布在线帖子。 我在QtCreator 5.1 编码。我使用的类就像下面的类。

下面的只是填充了一个任务列表,但是在整个8到12小时的过程中,程序占用的内存只是不断的上升,逐渐的上升。

我在使用这个类时做错了什么? 我必须能够像现在一样继续在线发帖,大约每 4 到 8 秒一次。

这是一个简单的类,用于处理我的一个流程:

头文件: tasklistprocess.h

#ifndef TASKLISTPROCESS_H
#define TASKLISTPROCESS_H

#include <QThread>
#include <QtCore>
#include <QNetworkRequest>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QListWidget>
#include <QTabWidget>

#include "globalhelper.h"
#include "securityinfo.h"

class TaskListProcess : public QThread

    Q_OBJECT
public:
    explicit TaskListProcess(QListWidget *obj_main, QTabWidget *tabs_main, QString user, QObject *parent = 0);

signals:
    void upTaskStorage(int key,QHash<QString,QString> item);

private:
    GlobalHelper gh;
    Securityinfo sci;
    QNetworkAccessManager *nam;
    QNetworkRequest request;
    QByteArray data;

    // this is the disposable params for reusage through out the class
    QUrlQuery params;
    QString post_data;
    QString user_name;
    QTimer *tasklist_tmr;

    bool get_task_list;

    QListWidget *obj;
    QTabWidget *tabs;

private slots:
    void setTaskList();
    void replyFinished(QNetworkReply *reply);
    void sendPost(QString file_name, QUrlQuery params);
;

#endif // TASKLISTPROCESS_H`

源文件:tasklistprocess.cpp

#include "tasklistprocess.h"

TaskListProcess::TaskListProcess(QListWidget *obj_main, QTabWidget *tabs_main, QString user, QObject *parent) :
    QThread(parent)

    user_name = user;
    get_task_list = false;
    obj = obj_main;
    tabs = tabs_main;
    tasklist_tmr = new QTimer(this);

    connect(this,SIGNAL(started()),this,SLOT(setTaskList()));
    connect(tasklist_tmr,SIGNAL(timeout()),this,SLOT(setTaskList()));

    nam = new QNetworkAccessManager(this);
    request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
    request.setRawHeader( "User-Agent" , "Mozilla Firefox" );
    // here we connect up the data stream and data reply signals
    connect(nam, SIGNAL(finished(QNetworkReply*)), this, SLOT(replyFinished(QNetworkReply*)));


void TaskListProcess::setTaskList()

    qDebug() << "Your task list was set";
    bool in = false;
    if(!(tasklist_tmr->isActive()))
    
        tasklist_tmr->start(10000);
        in = true;
    
    if(!(get_task_list))
    
        params.clear();
        params.addQueryItem("user_name", user_name);
        params.addQueryItem("logged_in", "1");
        sendPost("getTaskList.php",params);
        get_task_list = true;
    
    else
    
        if(post_data.contains("|*|"))
        
            //here i retrieve a piece of information from a php script which is stored in a custom string format
            // here we clear the list for the new data to be put in
            if(obj->count()>0)
            
                obj->clear();
            
            int key = 0;
            foreach(QString inner_task,post_data.split("|*|"))
            
                QHash<QString,QString> task_cont;
                //qDebug() << "         ";
                if(inner_task.contains("*,*"))
                
                    foreach(QString task_val,inner_task.split("*,*"))
                    
                        if(task_val.contains("*=*"))
                        
                            QStringList key_pairs = task_val.split("*=*");
                            task_cont.insert(key_pairs[0],key_pairs[1]);
                            if(key_pairs[0] == "tt")
                            
                                QString val_in;
                                if(key_pairs[1].length()>10)
                                
                                    // this sets the title to the shortened version
                                    // if the string length is too long
                                    val_in = key_pairs[1].left(10) + "....";
                                
                                else
                                
                                    val_in = key_pairs[1];
                                
                                obj->addItem("Task :" + QString::fromUtf8(key_pairs[1].toStdString().c_str()));
                            
                        
                    
                
                //task_storage.insert(key,task_cont);
                emit upTaskStorage(key,task_cont);
                key ++;
            
        
        get_task_list = false;
    
    // here we're checking to see if they are looking at the task tab so it doesn't keep changing
    // back and forth between the tabs
    bool change = true;
    if(tabs->currentIndex() != 0)
    
        change = false;
    

    if(change)
    
        tabs->setCurrentIndex(0);
    else if(in)
    
        tabs->setCurrentIndex(0);
    


void TaskListProcess::replyFinished(QNetworkReply *reply)

    if (reply->error() != QNetworkReply::NoError) 
            qDebug() << "Error in" << reply->url() << ":" << reply->errorString();
            return;
        
    QString data = reply->readAll().trimmed();
    post_data = data;
    if(get_task_list)
    
        setTaskList();
    


void TaskListProcess::sendPost(QString file_name, QUrlQuery params)

    post_data = "";
    QUrl url(sci.getHost() + file_name);

    url.setQuery(params);

    data.clear();
    data.append(params.toString().toUtf8());

    request.setUrl(url);
    nam->post(request, data);

【问题讨论】:

【参考方案1】:

来自 Qt 文档http://qt-project.org/doc/qt-5.1/qtnetwork/qnetworkaccessmanager.html

注意:请求完成后,由 用户在适当的时候删除 QNetworkReply 对象。不要 直接在连接到finished()的槽内删除它。你可以 使用 deleteLater() 函数。

我建议在您的 replyFinished() 方法中调用 reply-&gt;deleteLater()

【讨论】:

但如果我将其删除,那么如何在我的计时器的下一个间隔内重复使用它?我不会得到一个错误说对象不存在吗? replyFinished() 中作为参数的QNetworkReply 是由Qt 创建的。你不重复使用它。正如文档所说,您有责任释放它。 非常感谢您的帮助。我试试看能不能修好。【参考方案2】:

您应该在使用后为 QNetworkReply 对象调用 deleteLater()。

注意:请求完成后,用户有责任在适当的时候删除 QNetworkReply 对象。不要在连接到finished()的槽内直接删除它。您可以使用 deleteLater() 函数。

更多信息在这里:http://harmattan-dev.nokia.com/docs/library/html/qt4/qnetworkaccessmanager.html

【讨论】:

但如果我将其删除,那么如何在我的计时器的下一个间隔内重复使用它?我不会收到一个错误说对象不存在吗? 每次发布​​请求都会得到新的回复对象 非常感谢您的帮助。我试试看能不能修好。【参考方案3】:

谢谢大家的帮助。 很简单

中回复上的“deleteLater()”
void replyFinished(QNetworkReply *reply)



它应该看起来像这样

void replyFinished(QNetworkReply *reply)

    // after all of your processing 
    reply->deleteLater();

这是一个小问题,但让我发疯了很长时间,所以我希望这会有所帮助

【讨论】:

以上是关于发布请求和 QNetworkAccessManager 的内存泄漏的主要内容,如果未能解决你的问题,请参考以下文章

带有用户名和角色的 FastAPI 发布请求,怎么做?

带有标头和正文的 HTTPClient 发布请求

当内容类型和内容编码标头一起发送时,表达js拒绝发布请求(400错误请求)

Google Play 发布失败并显示“请求路径中的跟踪名称和请求正文必须匹配”

多个页面上的多个 ajax 和表单发布请求

Flask 和 Werkzeug:使用自定义标头测试发布请求