• 17476阅读
  • 9回复

[提问]qt5 多线程下载 QNetworkReply readAll后程序崩溃 [ [复制链接]

上一主题 下一主题
 

只看楼主 正序阅读 楼主  发表于: 2014-04-12
现在使用qt5.2做一个多线程下载的软件,使用的是QNetworkAccessManager、QNetworkRequest、QNetworkReply这三个类首先通过QNetworkReply的header函数获取到要下载的文件大小,然后分成多个线程下载,在下载的线程中,把reply的readyRead信号与写入文件的槽连接起来,每次有数据过来的时候就将数据写入文件中  
QNetworkAccessManager * manager = new QNetworkAccessManager;    QNetworkRequest request;    request.setUrl(url);
    QString strData = QString( "bytes=%0%1" ).arg( startByte ).arg( endByte );  
    request.setRawHeader("Range", strData.toLatin1() );  //toAscii   toLatin1
   //  request.setRawHeader("User-Agent" ,"Mozilla/5.0 Firefox/7.0.1 Chrome/24.0.1312.60 ");  
/*Range用于请求头中,指定第一个字节的位置和最后一个字节的位置*/  
/*if QT_VERSION > QT_VERSION_CHECK(5, 0, 0)*/
    reply = manager->get( request );    
    if( ! reply ) return;
    connect( reply, SIGNAL( readyRead() ), this, SLOT( writeToFile() ) );    
           connect(reply, SIGNAL(downloadProgress(qint64,qint64)),this,SLOT(downloadProgress(qint64,qint64)));  
connect( reply, SIGNAL( finished() ), this, SLOT( finishedDownload() ) );    
this->exec();
写入文件的代码如下:
void HttpThread::writeToFile()
{    
    if (reply->bytesAvailable()>0)    
    {        
        QByteArray tempArry = reply->readAll();
        lock.lockForWrite();        
        file->seek(this->startByte+this->doneBytes);        
        file->write(tempArry);        lock.unlock();      
        this->doneBytes += tempArry.size();        
        emit this->progressChanged(tempArry.size());    
    }
}
当reply发出finished信号后,结束本线程,调用deleteLater函数delete掉reply通知父类,如果所有线程都下载完成则关闭文件void HttpThread::finishedDownload()
{    
    reply->deleteLater();    
    emit finish(true);  
     this->quit();
}
目前发现的问题就出现在上面的写入文件中的reply->readAll()函数,只要使用了这个函数,程序就崩溃(下载小文件没事,下载大文件时间久了就崩溃),报的错误有三种:1)无效的指针(我记录了这个指针,每次都是不一样的),2)double free。。。,3)直接异常结束,没有错误提醒。
现在问题就是,reply的read、readAll、readData,ReadLine等读取数据的函数都使用过了,都会崩溃,如果不使用这几个函数的话程序是没有问题的(当然啦,读不了数据)连续下载20个以上的小文件是没有问题的,但是同时下载几个大于1G的文件基本就会崩溃我在下面会放上源代码,希望有了解这方面的大大可以给出比较详细的解决方法,谢谢大家
downloadtest.zip (53 K) 下载次数:115
(测试环境Qt5.2,LinuxDeepin,ubuntu12.04,fedora20)
ftpm

只看该作者 9楼 发表于: 2022-12-28
回 ubuntu爱好者 的帖子
ubuntu爱好者:此问题已经解决
原因是reply跟manager这两个对象是在run函数中new出来的,按照Qt官方的说法,继承于QThread的类只是线程的管理者而不是子线程本身,所以reply对象属于子线程,而reply->readAll函数则是在主线程中执行的(可以使用currentThreadId打印出run函数所处的线程ID还有 .. (2014-04-19 11:05) 

你好,还有源代码吗?现在下载的数据损坏,不能使用
离线pulller

只看该作者 8楼 发表于: 2017-09-30
感谢分享
离线mtvcsa

只看该作者 7楼 发表于: 2017-08-27
离线寻觅虚无

只看该作者 6楼 发表于: 2016-07-09
谢谢分享
离线wisteria

只看该作者 5楼 发表于: 2015-02-03
为什么我运行多线程的例子的时候,总是出现“multiple definition of ''main” "?
Winter in my heart

只看该作者 4楼 发表于: 2014-04-19
此问题已经解决
原因是reply跟manager这两个对象是在run函数中new出来的,按照Qt官方的说法,继承于QThread的类只是线程的管理者而不是子线程本身,所以reply对象属于子线程,而reply->readAll函数则是在主线程中执行的(可以使用currentThreadId打印出run函数所处的线程ID还有reply->readAll函数所处的线程,不管在哪个子线程对象中,reply->readAll函数打印的ID都会与主线程相同),问题正在于此,
根据http://www.qtcentre.org/threads/27112-I-think-there-is-a-problem-with-QNetworkReply-and-threads这篇帖子中六楼的Rembobo所说,子线程中的某些指针(reply)在子线程中已经成为无效指针的时候,在主线程中可能还被当作有效的指针,当去使用一个无效指针的时候,结果可想而知。。。
当然相反的情况也会发生,下面我会给出修改好的代码,希望对大家有帮助
(注:代码是同学从某个论坛下载回来测试的,用的是qt4.7,当然接受网络数据用的就不是QNetworkReply这几个类,我们是做了修改以符合最新的qt5.2,如果原作者正在阅读这篇帖子,请联系我)
修改完成的代码 downloadtest.tar.gz (41 K) 下载次数:299
4条评分好评度+1贡献值+1金钱+10威望+1
as56961 好评度 +1 - 2019-08-09
as56961 贡献值 +1 - 2019-08-09
as56961 威望 +1 - 2019-08-09
as56961 金钱 +10 - 2019-08-09
ftpm

只看该作者 3楼 发表于: 2014-04-15
回 chenjun0211 的帖子
chenjun0211:QByteArray tempArry = reply->readAll();改写成QByteArray tempArry = reply->read(reply->bytesAvailable());这样试试
小文件用readAll应该是没问题的,大文件传输不能一次完成,可能是这样的原因 (2014-04-12 16:58) 

我试过你说的这种方法了,还是不行,大文件下载程序直接是异常中止,没有报错,多个小文件同时下载就会报错,如下:
*** Error in `/home/yang/QtWorkSpace/PolarBearDownload/build-PolarBearDownload-Desktop_Qt_5_2_0_GCC_64bit-Release/PolarBearDownload': double free or corruption (!prev): 0x00007fb700007860 ***
======= Backtrace: =========
/lib/x86_64-linux-gnu/libc.so.6(+0x80a46)[0x7fb7846dfa46]
/usr/lib/nvidia-304/tls/libnvidia-tls.so.304.116(+0x1cd1)[0x7fb7825f2cd1]
======= Memory map: ========
00400000-00467000 r-xp 00000000 08:0e 2370542                            /home/yang/QtWorkSpace/PolarBearDownload/build-PolarBearDownload-Desktop_Qt_5_2_0_GCC_64bit-Release/PolarBearDownload
00667000-00669000 r--p 00067000 08:0e 2370542                            /home/yang/QtWorkSpace/PolarBearDownload/build-PolarBearDownload-Desktop_Qt_5_2_0_GCC_64bit-Release/PolarBearDownload
00669000-0066a000 rw-p 00069000 08:0e 2370542                            /home/yang/QtWorkSpace/PolarBearDownload/build-PolarBearDownload-Desktop_Qt_5_2_0_GCC_64bit-Release/PolarBearDownload
00dde000-016fc000 rw-p 00000000 00:00 0                                  [heap]
40808000-4080a000 r-xs 00000000 08:0e 146215                             /tmp/glWq3BOe (deleted)
40c94000-40d12000 rw-p 00000000 00:00 0
ftpm

只看该作者 2楼 发表于: 2014-04-15
好的,我现在试试,非常感谢
ftpm
离线chenjun0211

只看该作者 1楼 发表于: 2014-04-12
QByteArray tempArry = reply->readAll();改写成QByteArray tempArry = reply->read(reply->bytesAvailable());这样试试
小文件用readAll应该是没问题的,大文件传输不能一次完成,可能是这样的原因
快速回复
限100 字节
 
上一个 下一个