我正在做类似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也会出现同样的问题。我做错了什么,或者这可能是一个错误?
答案 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。