2017-03-05 170 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