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