查看完整版本: [-- qt5 多线程下载 QNetworkReply readAll后程序崩溃 [ --]

QTCN开发网 -> 《零基础学Qt4编程》专栏 -> qt5 多线程下载 QNetworkReply readAll后程序崩溃 [ [打印本页] 登录 -> 注册 -> 回复主题 -> 发表主题

ubuntu爱好者 2014-04-12 00:43

qt5 多线程下载 QNetworkReply readAll后程序崩溃 [

现在使用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的文件基本就会崩溃我在下面会放上源代码,希望有了解这方面的大大可以给出比较详细的解决方法,谢谢大家
[attachment=11951]
(测试环境Qt5.2,LinuxDeepin,ubuntu12.04,fedora20)

chenjun0211 2014-04-12 16:58
QByteArray tempArry = reply->readAll();改写成QByteArray tempArry = reply->read(reply->bytesAvailable());这样试试
小文件用readAll应该是没问题的,大文件传输不能一次完成,可能是这样的原因

ubuntu爱好者 2014-04-15 23:25
好的,我现在试试,非常感谢

ubuntu爱好者 2014-04-15 23:54
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

ubuntu爱好者 2014-04-19 11:05
此问题已经解决
原因是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,如果原作者正在阅读这篇帖子,请联系我)
[attachment=11967]

wisteria 2015-02-03 19:25
为什么我运行多线程的例子的时候,总是出现“multiple definition of ''main” "?

寻觅虚无 2016-07-09 12:48
谢谢分享

mtvcsa 2017-08-27 11:12

pulller 2017-09-30 10:29
感谢分享

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

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


查看完整版本: [-- qt5 多线程下载 QNetworkReply readAll后程序崩溃 [ --] [-- top --]



Powered by phpwind v8.7 Code ©2003-2011 phpwind
Gzip disabled