c++qtqtnetwork

How to wait for multiple QNetwork replies synchronously using QEventLoop?


My function downloads multiple files concurrently, using QNetworkAccessManager. And then it should wait for all the QNetworkReply replies to finish before continuing with the rest of the code.

However I can't get QEventLoop to work with multiple connections, it either returns before all the replies finish, or just gets stuck in what I assume is an infinite loop.

This is my code:

void DownloadFiles(QStringList urls)
{
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QEventLoop loop;
    QNetworkReply *replies[6];

    for (int i = 0, i < urls.lenght(); i++){
        replies[i] = manager->get(QNetworkRequest(QUrl(urls[i]));
        connect(replies[i], SIGNAL(finished()), &loop, SLOT(quit()));
    }
    loop.exec();  // Wait here
    // Rest of code
}

Another attempt, perhaps even more nonsensical:

void DownloadFiles(QStringList urls)
{
    QNetworkAccessManager *manager = new QNetworkAccessManager(this);
    QEventLoop loops[6];
    QNetworkReply *replies[6];

    for (int i = 0, i < urls.lenght(); i++){
        replies[i] = manager->get(QNetworkRequest(QUrl(urls[i]));
        connect(replies[i], SIGNAL(finished()), &loops[i], SLOT(quit()));
    }
    for (int i = 0; i < 6; i++)
       loops[i]->exec();  // Wait here
    // Rest of code
}

Again, my goal is to download multiple file concurrently, and wait for all of them to finish before continuing with the rest of the code.


Solution

  • You can use a counter to check how many times the signal was triggered.

    #include <QtNetwork>
    
    static void DownloadFiles(const QList<QUrl> & urls){
        QNetworkAccessManager manager;
        QList<QNetworkReply *> replies;
        QEventLoop loop;
        int total = urls.length();
        for(const QUrl & url: urls){
            QNetworkReply *reply = manager.get(QNetworkRequest(url));
            QObject::connect(reply, &QNetworkReply::finished, [&total, &loop](){
                total--;
                if(total == 0){
                    loop.quit();
                }
            });
            replies << reply;
        }
        loop.exec();
        for(QNetworkReply *reply: replies){
            qDebug() << "===============================";
            qDebug() << "url:" << reply->url() << "\nhas error?" << (reply->error() != QNetworkReply::NoError);
            qDebug() << "===============================";
        }
        qDebug() << "Completed";
        qDeleteAll(replies.begin(), replies.end());
        replies.clear();
    }
    
    int main(int argc, char *argv[]) {
        QCoreApplication app(argc, argv);
    
        QList<QUrl> urls { QUrl("https://www.qt.io/"),
                           QUrl("https://github.com/"),
                           QUrl("https://stackoverflow.com/") };
        DownloadFiles(urls);
    
        return 0;
    }
    

    output:

    ===============================
    url: QUrl("https://www.qt.io/") 
    has error? false
    ===============================
    ===============================
    url: QUrl("https://github.com/") 
    has error? false
    ===============================
    ===============================
    url: QUrl("https://stackoverflow.com/") 
    has error? false
    ===============================
    Completed