【问题标题】:QNetworkReply not being deletedQNetworkReply 未被删除
【发布时间】: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 也会出现同样的问题。我做错了什么,或者这可能是一个错误?

【问题讨论】:

    标签: python pyqt4 pyside qtnetwork


    【解决方案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 上都使用了。

    【讨论】:

      猜你喜欢
      • 2011-08-06
      • 1970-01-01
      • 2019-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-09
      相关资源
      最近更新 更多