【问题标题】:Download big files with QNetworkreply::readAll freeze for a few seconds使用 QNetworkreply::readAll 下载大文件冻结几秒钟
【发布时间】:2016-11-05 20:56:22
【问题描述】:

我使用了像 doc http://doc.qt.io/qt-4.8/qnetworkaccessmanager.html 这样的例子

我创建了一个startDownload

connect(pushButton, SIGNAL(clicked(bool)), this, SLOT(startDownload(bool)));

startDownload(bool)我放了这个:

file = new QFile("C:/foo/bar/bigfile.7z");
file->open(QIODevice::WriteOnly);

QNetworkRequest request;
request.setUrl(QUrl("http://localhost/bigfile.7z"));
request.setRawHeader("User-Agent", "MyOwnBrowser 1.0");

QNetworkReply *reply = manager->get(request);

connect(reply, SIGNAL(readyRead()), this, SLOT(slotReadyRead()));
connect(reply, SIGNAL(error(QNetworkReply::NetworkError)),
        this, SLOT(slotError(QNetworkReply::NetworkError)));
connect(reply, SIGNAL(sslErrors(QList<QSslError>)),
        this, SLOT(slotSslErrors(QList<QSslError>)));

slotReadyRead我放了这个:

file->write(reply->readAll());

但是下载到最后会出现2秒的小卡顿,然后恢复正常,下载完成。仅当我尝试传输的文件很大时才会出现此问题。

【问题讨论】:

  • 我试图为您的问题写一个minimal example。我计算了每个file-&gt;write(reply-&gt;readAll()) 调用的时间,大多数调用花费了 0 毫秒,最后一次调用(在下载结束时)没有你描述的显着差异(我尝试了我的本地文件的代码大约 1GB 大小的网络)。也许是代码中的另一件事使事情变慢?尝试测量函数调用的持续时间,以确保确实是这个调用导致了问题。
  • @Mike 这个问题只出现在 GUI 应用程序中,例如QMainWindow
  • 问题出现在 GUI 中,因为回调花费了 很多 时间来完成并返回到事件循环。在我的测试中,我只是测量在file-&gt;write(reply-&gt;readAll()); 中花费的时间,以确保下载结束时确实需要 2 秒。但事实证明,其实用不了那么长时间,你的输出不一样吗?
  • @Mike 18mb => i.stack.imgur.com/qSU2n.png in online server, 300mb in localhost => i.stack.imgur.com/m58Wn.png 我认为问题因网络而异。
  • @Mike 是的,看看没有 setReadBufferSize => i.stack.imgur.com/OxhdH.png, with setReadBufferSize => i.stack.imgur.com/bMZQQ.png 我正在做一些测试,会准备一个答案。

标签: qt qwidget qtnetwork


【解决方案1】:

这是预期的行为。 QIODevice::readAll() 将阻塞线程直到下载完成。 QFile::write() 也可能会阻塞,具体取决于磁盘速度和缓存策略。如果文件足够大,readAll() 方法也可能会消耗相当多的 RAM。

最简单的解决方案是使用read() 而不是readAll() 以更小的块下载文件。

话虽如此,要找到从网络读取并写入磁盘的完美缓冲区大小并不容易;这将取决于网络连接如何响应与磁盘写入速度。

【讨论】:

  • 有道理,能举个例子吗?谢谢!
  • 或者如果你想使用阻塞api,不要阻塞主线程——使用工作线程。
  • 必须在询问之前阅读文档。这就是拥有文档的全部意义所在。您只能就更复杂或未涵盖的主题提出问题,或者您是否遇到问题。这个网站不是为了帮助那些懒得阅读文档的人。很多人都懒得看,也懒得找,直接问。那只是懒惰。它不应该得到奖励。它不会帮助你变得更好,它会让你更懒惰。
  • 这不是什么复杂的事情,把下载的代码放在一个QObject中,创建对象和线程,把对象移动到线程中,然后它的函数就会在那个线程中执行。使用信号排队下载,下载完成时使用信号。那是简单的事件驱动异步编程。如果您遇到特定问题并陷入困境,他们会提出问题,但仅在您搜索之后。并且不要亲自投票,它们不是个人的,这只是意味着您的问题不符合社区的标准。但有时它只是“猴子看猴子做”;)
  • readAll() 被记录为从设备返回剩余数据。它确实阻塞线程。查看它的implementation,它只是调用read() 来填充数组。
【解决方案2】:

在尝试使用@Mike代码后,我注意到readyReadfinal中读取的数据比之前的要高很多,导致写入文件很慢:

最后两个读数是:

  1. 46080000 字节 - 写入需要约 1.6 秒。
  2. 227323951 字节 - 写入需要约 2.7 秒。

它根据网络类型和速度而变化,允许缓冲区是否很大。

在 GUI 应用程序中会导致大约 4 秒的“冻结”感。

对于我使用QNetworkReply::setReadBufferSize 的有限缓冲区,请查看区别:

读数为 1048576 字节 - 写入需要 2 到 10 毫秒。

【讨论】:

  • @KubaOber 我明白了,但这是唯一有效的方法,我遵循了@ddriver 的指导,你能告诉我一个更好的方法吗?
  • 您正在泄露网络访问管理器的实例。由于下载工作者总是需要网络访问管理器,您可以将其添加为成员并按值保存,无需在每次调用 process() 时重新创建它。
猜你喜欢
  • 1970-01-01
  • 2017-04-06
  • 1970-01-01
  • 1970-01-01
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多