2017-03-05 4 views
0

Ich versuche gerade, eine Software zu erstellen, die viele Dateien von Google Drive herunterlädt. Das Herunterladen ist derzeit kein Problem.Fehler beim Starten von QThread bei Verwendung von QNetworkAccessManager

Dennoch, ich stoße auf ein Problem beim Starten von mehr als 500 gleichzeitige Downloads. Ich benutze eine leicht modifizierte Version dieses Tutorials: https://wiki.qt.io/Download_Data_from_URL. Hier

ist die h-Datei:

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); 
}; 

Und hier ist die CPP-Datei:

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; 
} 

Die Frage ist "QThread :: start: Fehler Thread erstellen()", wenn erreicht ungefähr den 500. Download. Ich habe versucht, die Anzahl der gleichzeitig laufenden Downloads zu begrenzen - aber ich bekomme immer das gleiche Problem. Außerdem habe ich versucht, jeden Downloader beim Beenden seiner Aufgabe zu löschen - es hat nichts anderes getan, als das Programm zu stürzen;)

Ich denke, dass es von der Anzahl der Threads kommt, die für einen einzigen Prozess erlaubt sind, aber ich bin nicht in der Lage um es zu lösen!

Hat jemand eine Idee, die mir helfen könnte?

Vielen Dank!

+3

Starten Sie Ihre eigenen Threads, um Anfragen auszuführen?Oder haben Sie mehrere Instanzen von 'QNetworkAccessManager' (möglicherweise einen für jede Anfrage)? Sie brauchen nicht beides für Ihr Ziel. ** Sie benötigen nur eine Instanz von 'QNetworkAccessManager' und Ihren Haupt-Thread ** (und nichts mehr). Verwenden Sie die asynchrone API, die QNetworkAccessManager bereitstellt. Lassen Sie Qt niedrigstufige Details der Parallelisierungsanforderungen behandeln, wenn möglich und Sie sollten in Ordnung sein. – Mike

+0

Ich habe mehrere Instanzen von QNetworkAccessManager, aber nur den Hauptthread. Wenn ich versuche, nur eine (statische) Instanz von QNetworkAccessManager zu verwenden, hat mein Programm ein merkwürdiges Verhalten. Es funktioniert nicht mehr, Dateien werden sofort ohne Inhalt heruntergeladen ... Und es gibt viel mehr Dateien als erwartet! – Abrikot

+0

** Sie müssen eine [MCVE] (https://stackoverflow.com/help/mcve) hinzufügen, damit Ihre Frage beantwortbar ist. ** Sie müssen etwas falsch in Ihrem Code tun, damit dies geschieht. – Mike

Antwort

2

QNetworkAccessManager::finished Signal wird dokumentiert als wird ausgegeben, wenn eine ausstehende Netzwerkantwort beendet ist.

Das bedeutet, wenn die QNetworkAccessManager verwendet wird, um mehrere Anfragen gleichzeitig auszuführen (und dies ist). finished Signal wird einmal für jede Anfrage ausgegeben. Da Sie eine gemeinsame Instanz von QNetworkAccessManager zwischen Ihren FileDownloader Objekten haben, wird das finished Signal für jeden von Ihnen getätigten Anruf get ausgegeben. So erhalten alle Objekte ein finished Signal, sobald der erste das Herunterladen beendet.

Anstatt QNetworkAccessManager::finished zu verwenden, können Sie QNetworkReply::finished verwenden, um Signalverwechslungen zu vermeiden. Hier ist ein Beispiel-Implementierung:

#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" 

Wenn Sie diese Methode verwenden, um große Dateien herunterladen, sollten Sie einen Blick auf this question haben.

+0

Danke, es hat funktioniert! Ich wusste nicht, QNetworkReply hatte ein Signal "fertig" ... – Abrikot

0

Eine Lösung könnte sein, eine QThreadPool zu verwenden. Sie füttern es einfach Aufgaben (QRunnable) und es wird die Anzahl der Threads und die Aufgabenwarteschlange für Sie behandeln.

In Ihrem Fall ist dies jedoch nicht perfekt, da Sie die Anzahl der gleichzeitigen Downloads auf die Anzahl der von QThreadPool erstellten Threads begrenzen werden (im Allgemeinen die Anzahl der CPU-Kerne, die Sie haben).

Um dies zu umgehen, müssen Sie QThread selbst behandeln und QThreadPool nicht verwenden. Sie würden eine kleine Anzahl von Threads erstellen (siehe QThread::idealThreadCount()) und mehrere FileDownloader auf jedem QThread ausführen.

+0

** Bitte beachten Sie, dass es in der Regel nicht notwendig ist, ein 'QThreadPool' zu verwenden, wenn 'QNetworkAccessManager' verwendet wird. ** Aus der Dokumentation: *" QNetworkAccessManager reiht die Anfragen Er empfängt. Die Anzahl der parallel ausgeführten Anforderungen hängt vom Protokoll ab. Derzeit werden für das HTTP-Protokoll auf Desktop-Plattformen 6 Anfragen parallel für eine Host/Port-Kombination ausgeführt. "*. 'QNetworkAccessManager' verarbeitet bereits diese Details auf niedriger Ebene. Es ist intern so konzipiert, dass es der Arbeitsweise von QtWebKit entspricht, siehe [hier] (https://blog.qt.io/blog/2011/04/29/threaded-http-inside- qnetworkaccessmanager /). – Mike

Verwandte Themen