2017-03-05 172 views
0

我目前正在试图制作一个软件,可从Google Drive下载大量文件。下载目前不是问题。使用QNetworkAccessManager时无法启动QThread

不过,我在启动500多个同时下载时遇到问题。我使用本教程的稍微修改版本:https://wiki.qt.io/Download_Data_from_URL

这里是.h文件:

class FileDownloader : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit FileDownloader(QUrl url, QObject *parent = 0, int number = 0); 
    QByteArray downloadedData() const; 
    void launchNewDownload(QUrl url); 
    QByteArray m_DownloadedData; 
    QNetworkReply* reply; 

    static QNetworkAccessManager *m_WebCtrl; 

signals: 
    void downloaded(); 

private slots: 
    void fileDownloaded(QNetworkReply* pReply); 
}; 

这里是.cpp文件:

QNetworkAccessManager* FileDownloader::m_WebCtrl = nullptr; 

FileDownloader::FileDownloader(QUrl url, QObject *parent) : 
    QObject(parent) 
{ 
    if (m_WebCtrl == nullptr) { 
     m_WebCtrl = new QNetworkAccessManager(this); 
    } 
    connect(m_WebCtrl, SIGNAL (finished(QNetworkReply*)),this, SLOT (fileDownloaded(QNetworkReply*))); 

    launchNewDownload(url); 
} 

void FileDownloader::launchNewDownload(QUrl url) { 
    QNetworkRequest request(url); 
    this->reply = m_WebCtrl->get(request); 
} 

void FileDownloader::fileDownloaded(QNetworkReply* pReply) { 
    m_DownloadedData = pReply->readAll(); 

    //emit a signal 
    pReply->deleteLater(); 
    emit downloaded(); 
} 

QByteArray FileDownloader::downloadedData() const { 
    return m_DownloadedData; 
} 

的问题是 “的QThread ::开始:无法创建线程()” 时,达到了第500次下载。我试图限制同时运行的下载数量 - 但我总是遇到同样的问题。此外,我试图在完成任务时删除每个下载器 - 它没有别的事情比崩溃的程序;)

我认为它来自一个唯一的过程允许的线程数,但我不能解决它!

有没有人有一个想法,可以帮助我?

谢谢!

+3

你开始自己的线程来运行请求?或者你有多个'QNetworkAccessManager'实例(每个请求可能有一个)?您的目标不需要以上两者。 **你只需要'QNetworkAccessManager'的一个实例和你的主线程**(除此之外)。使用异步API'QNetworkAccessManager'提供。让Qt在可能的情况下处理并发请求的底层细节,你应该没问题。 – Mike

+0

我有QNetworkAccessManager的多个实例,但只有主线程。 当我尝试仅使用QNetworkAccessManager的一个(静态)实例时,我的程序有一个奇怪的行为。它不再工作,文件立即被下载,没有任何内容......并且有更多的文件比预期的更多! – Abrikot

+0

**您需要在您的问题中添加一个[MCVE](https://stackoverflow.com/help/mcve)以便负责。**您必须在代码中做错某些事情才会发生。 – Mike

回答

2

QNetworkAccessManager::finished无论何时待处理的网络应答完成,信号被记录为

这意味着如果使用QNetworkAccessManager一次运行多个请求(并且这是perfectly normal usage)。 finished信号将为每个请求发射一次。由于您的FileDownloader对象之间共享了QNetworkAccessManager实例,因此您拨打的每个get电话都会发出finished信号。因此,只要第一个FileDownloader完成下载,所有FileDownloader对象都会得到finished信号。

除了使用QNetworkAccessManager::finished之外,您可以使用QNetworkReply::finished来避免混合信号。下面是一个示例实现:

#include <QtNetwork> 
#include <QtWidgets> 

class FileDownloader : public QObject 
{ 
    Q_OBJECT 
    //using constructor injection instead of a static QNetworkAccessManager pointer 
    //This allows to share the same QNetworkAccessManager 
    //object with other classes utilizing network access 
    QNetworkAccessManager* m_nam; 
    QNetworkReply* m_reply; 
    QByteArray m_downloadedData; 

public: 
    explicit FileDownloader(QUrl imageUrl, QNetworkAccessManager* nam, 
          QObject* parent= nullptr) 
     :QObject(parent), m_nam(nam) 
    { 
     QNetworkRequest request(imageUrl); 
     m_reply = m_nam->get(request); 
     connect(m_reply, &QNetworkReply::finished, this, &FileDownloader::fileDownloaded); 
    } 
    ~FileDownloader() = default; 

    QByteArray downloadedData()const{return m_downloadedData;} 

signals: 
    void downloaded(); 
private slots: 
    void fileDownloaded(){ 
     m_downloadedData= m_reply->readAll(); 
     m_reply->deleteLater(); 
     emit downloaded(); 
    } 
}; 

//sample usage 
int main(int argc, char* argv[]){ 
    QApplication a(argc, argv); 

    QNetworkAccessManager nam; 
    FileDownloader fileDownloader(QUrl("http://i.imgur.com/Rt8fqpt.png"), &nam); 
    QLabel label; 
    label.setAlignment(Qt::AlignCenter); 
    label.setText("Downloading. . ."); 
    label.setMinimumSize(640, 480); 
    label.show(); 
    QObject::connect(&fileDownloader, &FileDownloader::downloaded, [&]{ 
     QPixmap pixmap; 
     pixmap.loadFromData(fileDownloader.downloadedData()); 
     label.setPixmap(pixmap); 
    }); 

    return a.exec(); 
} 

#include "main.moc" 

如果您使用此方法下载大文件时,请考虑一下this question

+0

谢谢,它的工作!我不知道QNetworkReply有一个信号“完成”... – Abrikot

0

一个解决方案可能是使用QThreadPool。您只需为它提供任务(QRunnable),它就会为您处理线程数和任务队列。

但在你的情况下,这并不完美,因为你将限制同时下载的数量与QThreadPool创建的线程数量(通常是你拥有的CPU核心数量)。

为了克服这个问题,你必须自己处理QThread,而不是使用QThreadPool。您将创建少量线程(请参阅QThread::idealThreadCount())并在每个QThread上运行多个FileDownloader

+0

**请注意,使用QNetworkAccessManager时通常不需要使用QThreadPool **从文档:它接收,并行执行的请求数量取决于协议,目前,对于桌面平台上的HTTP协议,对于一个主机/端口组合,并行执行6个请求。 'QNetworkAccessManager'已经处理了这些低层次的细节,它的内部设计是为了适应QtWebKit的工作方式,参见[这里](https://blog.qt.io/blog/2011/04/29/threaded-http-inside- qnetworkaccessmanager /)。 – Mike