QNetworkReply 未被删除

Posted

技术标签:

【中文标题】QNetworkReply 未被删除【英文标题】:QNetworkReply not being deleted 【发布时间】:2014-05-01 20:06:43 【问题描述】:

我正在做类似于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。事实上,由于这不会进行连接保持活动,如果我检查“netstat”,我会看到处于 TIME_WAIT 状态的僵尸 TCP 连接数量稳步增加,并最终在 30,000 多个端口时开始出现 PySide.QtNetwork.QNetworkReply.NetworkError.UnknownNetworkError已用完;进一步证明 QNetworkReply 没有被正确释放。

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

【问题讨论】:

【参考方案1】:

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

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

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

不过,解决方法是在 QNetworkReply 上调用 setParent(None)。这可以在任何时候完成,即使它是第一次从 QNetworkAccessManager 返回时。它最初是经理的父母;将 parent 更改为 null 意味着 Qt 不负责内存管理,它会被 Python 正确处理,即使不使用 deleteLater。

(我在网上的某个地方找到了这个提示;不幸的是我现在找不到它,或者我会链接它。)

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

编辑 2:我在 Python 2 上使用 PyQt4 没有泄漏,但我在 PySide 和 Python 3 上都使用了。

【讨论】:

以上是关于QNetworkReply 未被删除的主要内容,如果未能解决你的问题,请参考以下文章

QEventLoop等待另外一个事件的停止,非常实用 good

克隆虚拟机

php 获取2018年10月未被管理员创建的未被删除的重力表单条目

QNetworkReply 没有得到任何标题

QNetworkReply 等待完成

Expo Locations.watchPositionAsync 未被删除