QT环境下的多线程下载实现
来源:互联网 发布:同步带长度计算软件 编辑:程序博客网 时间:2024/05/27 19:26
QT环境下的多线程下载一般有两种实现方式:一种是采用QNetworkAccessManager的基础上,自己写下载代码,此方案的好处是什么都可以自己控制,缺点是下载速度慢、编码工作量大;第二种是使用QProcess调用外部程序实现,好处是直接调用成熟的第三方下载工具,下载速度快,代码量少。本文主要介绍第二种方式实现,调用Axel进行下载。之所以选择Axel,原则在于Axel的优点就是参数简单,多线程,下载速度快,尤其是下载大文件优势明显。
代码实现思路:初始化5个QProcess做为下载进程,组成一个简单的下载池。当需要下载文件时,检查下载池是否有QProcess下载进程空闲,如果有,就调用进行下载,同时如果没有,就保存到等待下载列表中。每个下载进程会在下载完成后发出void finished(int exitCode, QProcess::ExitStatus exitStatus)信号。程序接收到这个信号后,发出“已经下好一个文件”的信号通知别的进程,同时检查下载列表是否为空,如果不为空,就重新检查是否有空闲的下载进程,如果有,则下载。
核心代码见下面。
#include "dsdownload.h"#include "common/dsconfig.h"#include "common/dscommon.h"#include "net/dsnetwork.h"#include <QApplication>#include <QFile>DsDownload::DsDownload(QObject *parent) : QObject(parent){ for(int i = 0 ; i < 5; i++) { //初始下载任务池大小为5 QProcess *p = new QProcess(this); connect(p, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(downloadProcessFinished(int, QProcess::ExitStatus))); pPool.append(p); }//为避免阻塞,使用信号的方式通知下载进程进行下载 connect(this, SIGNAL(needStartDownloadFile(QString)), this, SLOT(startDownloadFile(QString))); connect(this, SIGNAL(finishedDownloadFile(QString)), this, SLOT(checkNeedDownloadFileSlot()));}//下载参数,-n 2:同时2个线程;-o %1:保存目录,%1会下面被替换; %2:下载的链接const QString DsDownload::axelCmdBase = "axel -n 2 -o %1 %2";/***下载实现*/void DsDownload::startDownloadFile(QString filename) { log.debug(Q_FUNC_INFO, tr("start... download file: %1").arg(filename)); QString downUrl = this->needDownloadFileMap.value(filename);//为避免进程可能被占用,重新取一次 QProcess *p = NULL; int i = 0; foreach (QProcess *pp, pPool) { if(pp->state() == QProcess::NotRunning) { log.debug(Q_FUNC_INFO, tr("download pool index: %1").arg(i)); p = pp; break; } ++i; } if(p != NULL && p->state() == QProcess::NotRunning) { QString axelCmd(tr(DsDownload::axelCmdBase.toAscii()) .arg("savePath", downUrl)); log.debug(Q_FUNC_INFO, "axelCmd: " + axelCmd); p->start(axelCmd); //等待开始 if(! p->waitForStarted()) { log.error(Q_FUNC_INFO, tr("Start process failed when download file: %1").arg(axelCmd)); return; }//保存文件和下载进程的关系,以便下载完成后,更新数据库使用 pMap.insert(filename, p); } else { //如果没有下载进程空闲,就把文件重新保存到下载列表中 //因为上面使用的是:QString filename = needDownloadFileList.takeFirst(); this->needDownloadFileList.insert(0, filename); }}/***下载进程完成后,发会出void finished(int exitCode, QProcess::ExitStatus exitStatus)信号*这里对应的接收SLOT*/void DsDownload::downloadProcessFinished ( int exitCode, QProcess::ExitStatus exitStatus ) { log.debug(Q_FUNC_INFO, tr("一个下载进程结束. exitCode: %1, exitStatus: %2") .arg(exitCode) .arg(exitStatus));//因为不知道是哪个下载进程结束,所以只好循环查询“文件-进程”对应的MAP QMapIterator<QString, QProcess *> i(pMap); while (i.hasNext()) { i.next(); QString filename = i.key(); QProcess *p = i.value(); QString filename = this->needDownloadFileMap.value(filename); //通过检查进程的状态,查看下载是否结束,还可以通过判断下载文件的临时文件是否存在来判断(axel会产生".st"结尾的临时文件) if (p->state() != QProcess::NotRunning) { continue; } log.debug(Q_FUNC_INFO, tr("file downloaded: %1") .arg(filename)); //移除文件-下载进程的对应关系 pMap.remove(filename);//移除文件-下载地址的对应关系 this->needDownloadFileMap.remove(filename); QVariantList pList; pList.append(filename); //更新数据库中文件的状态为2:表示已经下载完成 db.doPrepare("update resource set status=2 where filename=?", pList); //发出下载完成的信号,一是由其它程序去处理(比如是否开始播放) //二是让程序检查是否还有需要下载的文件 emit this->finishedDownloadFile(filename); } log.debug(Q_FUNC_INFO, tr("finished"));}
以上的代码,实现了下载列表分开(保存到数据库),进程池调用,由第三方下载工具axel确保下载速度,同时下载完后会第一时间发出通知,不会造成阻塞。
以前在网上参考过一段代码,它的实现是使用循环来等待程序结束,300ms让Ui响应一次,这样会导致前台展示出现非常明显的卡顿现象,影响用户体验。经过排查发现,平时CPU占用一般在30%左右,下载时CPU只占用2%左右,也就是说,下载进程阻塞了前台的UI进程。贴出以前参考的代码,供对比。
bool enstCdRecord::CreateCd(const QString &pImageFile) {QStringList cmdlist;cmdlist.append("-v");cmdlist.append("speed=2");cmdlist.append(pImageFile);mProcess.start("cdrecord.exe", cmdlist);while (! mProcess.waitForFinished(300)) { //启动程序后,用循环等待其结束,如果对程序何时结束并不关心,以下代码可以不需要。if (mProcess.state() == QProcess::NotRunning) ...{ //process failed QMessageBox::critical(mParent, SYSTEMNAME, tr("Error when record cd.")); return false;}qApp->processEvents(); //防止UI死锁,一般情况下,用这种等一小段时间(这里是300ms),让UI响应一次的办法,已经足够使用了。}if (mProcess.exitCode() != 0) { //error when run process QMessageBox::critical(mParent, SYSTEMNAME, tr("Error when record cd.")); return false;}return true;}
- QT环境下的多线程下载实现
- 多线程环境下的单例实现
- VS2013的开发环境下Qt的下载、安装、配置
- Qt的多线程实现
- Qt多线程的实现
- Qt实现多线程下的信号与槽通讯
- 安卓系统下的多线程断点下载实现
- 在Http协议下实现多线程断点的下载
- QT环境下实现UI界面的拼图
- QT环境下实现UI界面的“拼图游戏”
- 多线程环境下使用的单例模式的实现
- java多线程环境下单利模式的实现
- C++在Windows环境下多线程自动锁的实现
- Java实现多线程环境下的计数器功能
- Qt---多线程的简单实现
- Qt---多线程的简单实现
- 多线程下的下载原理
- QT:多线程下载文件
- <script>位置
- linux常用性能监控命令
- 用标签搜索的方式搜索百度电影
- c++ 23种设计模式之抽象工厂模式
- 修改manager server端口与appserver rest端口
- QT环境下的多线程下载实现
- MFC读取多文件与文件夹
- 如何成为好的程序员
- BES 503上查看 http 访问情况
- Android 控件中事件响应的三种实现方式
- 在Drupal当中,如何实现初始页面的帮助提示(Overlay help tips)?
- jquery扩展 详细(包含一些高级使用)
- 中国各省所处的经纬度范围
- 资源整合---------------------------MyHao123