QNetworkReply未被删除

时间:2014-05-01 20:06:43

标签: python pyqt4 pyside qtnetwork

我正在做类似this question的事情,但有一个更微妙的问题。

我有一个发出HTTP请求的API客户端类;我将QNetworkReply存储在对象中,以便从连接到“完成”信号的插槽访问其数据。在下一个请求中,它将被下一个QNetworkReply替换,因此Python应该能够释放先前的请求对象,从而释放底层网络资源。相反,旧的回复对象似乎卡在某处,导致内存泄漏,如果应用程序运行时间足够长,退出延迟,可能是因为所有发出的请求最终都被删除了。

简化但完整的例子:

import sys, signal
from PySide import QtCore, QtNetwork

class Poller(QtCore.QObject):
    url = QtCore.QUrl("http://localhost:5000/")

    def __init__(self, parent=None):
        super(Poller,self).__init__(parent)
        self.manager = QtNetwork.QNetworkAccessManager()

    def start(self):
        request = QtNetwork.QNetworkRequest(self.url)
        self.reply = self.manager.get(request)
        self.reply.finished.connect(self.readReply)
        self.reply.error.connect(self.error)

    def readReply(self):
        text = self.reply.readAll()
        self.reply.close() # doesn't help
        self.reply.deleteLater() # doesn't help
        QtCore.QTimer.singleShot(10, self.start)

    @QtCore.Slot(QtNetwork.QNetworkReply.NetworkError)
    def error(self, err):
        self.reply.finished.disconnect()
        sys.stderr.write('Error: ' + str(err) + '\n')
        QtCore.QTimer.singleShot(10, self.start)

signal.signal(signal.SIGINT, signal.SIG_DFL)
app = QtCore.QCoreApplication(sys.argv)
p = Poller()
p.start()
app.exec_()

被轮询的http服务器并不重要;对于这个测试我正在使用the Flask hello world。事实上,由于这不进行连接keepalive,如果我检查“netstat”,我看到在TIME_WAIT状态下稳定增加的僵尸TCP连接数,并最终开始一次30,000+端口获取PySide.QtNetwork.QNetworkReply.NetworkError.UnknownNetworkError已经用完了;进一步证明QNetworkReply未被正确释放。

PySide或PyQt4也会出现同样的问题。我做错了什么,或者这可能是一个错误?

1 个答案:

答案 0 :(得分:0)

首先,结果显示TCP连接在关闭后在TIME_WAIT中停留一段时间是正常的。我只是达到了极限,因为我已经将singleShot计时器设置为0ms进行测试。

我用C ++重写了这个例子。 deleteLater按预期工作,我可以通过省略来重现内存增长和缓慢退出。 (由于内存由Qt管理,所以必须通过QNetworkAccessManager析构函数删除所有回复对象。)

有趣的是,仔细研究一下,当使用deleteLater时,Python中不会发生缓慢的退出,但内存增长确实如此。所以我猜C ++对象正在被删除,但仍有资源在某处使用。这对我来说仍然很神秘。

但是,修复是在QNetworkReply上调用setParent(None)。这可以在任何时候完成,即使它首次从QNetworkAccessManager返回。它最初是经理的父母;将父级更改为null意味着Qt不负责内存管理,即使不使用deleteLater,它也将由Python正确处理。

(我在网上发现了这个提示;遗憾的是我现在无法找到它,或者我会链接它。)

编辑:我认为这在我的测试中有效,但我的应用程序仍在泄漏内存。

编辑2:我在Python 2上没有使用PyQt4泄漏,但是我使用PySide,而且使用Python 3。